/* * 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); }