It uses edge extrapolation code from Gimp's antialias plugin now, which has advantage of giving symmetrical results, which makes it possible to add two antialiased ID masks and have a constant 1.0 all over the frame. But has difference from the old implementation because it uses 3x3 matrix only, which doesn't give so much smooth looking edges. Perhaps it's not so bad, since if edges are really need to be smooth one might use Blur node. Another advantage is that the node is now nicely threaded. Reviewers: campbellbarton Reviewed By: campbellbarton Subscribers: ania Differential Revision: https://developer.blender.org/D1617
153 lines
4.9 KiB
C++
153 lines
4.9 KiB
C++
/*
|
|
* 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(s): Jeroen Bakker
|
|
* Monique Dewanchand
|
|
* Sergey Sharybin
|
|
*/
|
|
|
|
#include "COM_AntiAliasOperation.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
extern "C" {
|
|
# include "RE_render_ext.h"
|
|
}
|
|
|
|
|
|
/* An implementation of the Scale3X edge-extrapolation algorithm.
|
|
*
|
|
* Code from GIMP plugin, based on code from Adam D. Moss (adam@gimp.org)
|
|
* licensed by the MIT license.
|
|
*/
|
|
static int extrapolate9(float *E0, float *E1, float *E2,
|
|
float *E3, float *E4, float *E5,
|
|
float *E6, float *E7, float *E8,
|
|
const float *A, const float *B, const float *C,
|
|
const float *D, const float *E, const float *F,
|
|
const float *G, const float *H, const float *I)
|
|
{
|
|
#define PEQ(X,Y) (fabsf(*X - *Y) < 1e-3f)
|
|
#define PCPY(DST,SRC) do{*DST = *SRC;}while(0)
|
|
if ((!PEQ(B,H)) && (!PEQ(D,F))) {
|
|
if (PEQ(D,B)) PCPY(E0,D); else PCPY(E0,E);
|
|
if ((PEQ(D,B) && !PEQ(E,C)) || (PEQ(B,F) && !PEQ(E,A)))
|
|
PCPY(E1,B); else PCPY(E1,E);
|
|
if (PEQ(B,F)) PCPY(E2,F); else PCPY(E2,E);
|
|
if ((PEQ(D,B) && !PEQ(E,G)) || (PEQ(D,H) && !PEQ(E,A)))
|
|
PCPY(E3,D); else PCPY(E3,E);
|
|
PCPY(E4,E);
|
|
if ((PEQ(B,F) && !PEQ(E,I)) || (PEQ(H,F) && !PEQ(E,C)))
|
|
PCPY(E5,F); else PCPY(E5,E);
|
|
if (PEQ(D,H)) PCPY(E6,D); else PCPY(E6,E);
|
|
if ((PEQ(D,H) && !PEQ(E,I)) || (PEQ(H,F) && !PEQ(E,G)))
|
|
PCPY(E7,H); else PCPY(E7,E);
|
|
if (PEQ(H,F)) PCPY(E8,F); else PCPY(E8,E);
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
#undef PEQ
|
|
#undef PCPY
|
|
}
|
|
|
|
AntiAliasOperation::AntiAliasOperation() : NodeOperation()
|
|
{
|
|
this->addInputSocket(COM_DT_VALUE);
|
|
this->addOutputSocket(COM_DT_VALUE);
|
|
this->m_valueReader = NULL;
|
|
this->setComplex(true);
|
|
}
|
|
|
|
void AntiAliasOperation::initExecution()
|
|
{
|
|
this->m_valueReader = this->getInputSocketReader(0);
|
|
}
|
|
|
|
void AntiAliasOperation::executePixel(float output[4],
|
|
int x, int y,
|
|
void *data)
|
|
{
|
|
MemoryBuffer *input_buffer = (MemoryBuffer *)data;
|
|
const int buffer_width = input_buffer->getWidth(),
|
|
buffer_height = input_buffer->getHeight();
|
|
if (y < 0 || y >= buffer_height || x < 0 || x >= buffer_width) {
|
|
output[0] = 0.0f;
|
|
}
|
|
else {
|
|
const float *buffer = input_buffer->getBuffer();
|
|
const float *row_curr = &buffer[y * buffer_width];
|
|
if (x == 0 || x == buffer_width - 1 ||
|
|
y == 0 || y == buffer_height - 1)
|
|
{
|
|
output[0] = row_curr[x];
|
|
return;
|
|
}
|
|
const float *row_prev = &buffer[(y - 1) * buffer_width],
|
|
*row_next = &buffer[(y + 1) * buffer_width];
|
|
float ninepix[9];
|
|
if (extrapolate9(&ninepix[0], &ninepix[1], &ninepix[2],
|
|
&ninepix[3], &ninepix[4], &ninepix[5],
|
|
&ninepix[6], &ninepix[7], &ninepix[8],
|
|
&row_prev[x - 1], &row_prev[x], &row_prev[x + 1],
|
|
&row_curr[x - 1], &row_curr[x], &row_curr[x + 1],
|
|
&row_next[x - 1], &row_next[x], &row_next[x + 1]))
|
|
{
|
|
/* Some rounding magic to so make weighting correct with the
|
|
* original coefficients.
|
|
*/
|
|
unsigned char result = ((3*ninepix[0] + 5*ninepix[1] + 3*ninepix[2] +
|
|
5*ninepix[3] + 6*ninepix[4] + 5*ninepix[5] +
|
|
3*ninepix[6] + 5*ninepix[7] + 3*ninepix[8]) * 255.0f +
|
|
19.0f) / 38.0f;
|
|
output[0] = result / 255.0f;
|
|
}
|
|
else {
|
|
output[0] = row_curr[x];
|
|
}
|
|
}
|
|
}
|
|
|
|
void AntiAliasOperation::deinitExecution()
|
|
{
|
|
this->m_valueReader = NULL;
|
|
}
|
|
|
|
bool AntiAliasOperation::determineDependingAreaOfInterest(
|
|
rcti *input,
|
|
ReadBufferOperation *readOperation,
|
|
rcti *output)
|
|
{
|
|
rcti imageInput;
|
|
NodeOperation *operation = getInputOperation(0);
|
|
imageInput.xmax = input->xmax + 1;
|
|
imageInput.xmin = input->xmin - 1;
|
|
imageInput.ymax = input->ymax + 1;
|
|
imageInput.ymin = input->ymin - 1;
|
|
return operation->determineDependingAreaOfInterest(&imageInput,
|
|
readOperation,
|
|
output);
|
|
}
|
|
|
|
void *AntiAliasOperation::initializeTileData(rcti *rect)
|
|
{
|
|
return getInputOperation(0)->initializeTileData(rect);
|
|
}
|