Changeset View
Changeset View
Standalone View
Standalone View
source/blender/compositor/operations/COM_SMAAOperation.cc
| Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| #define SMAA_AREATEX_MAX_DISTANCE 20 | #define SMAA_AREATEX_MAX_DISTANCE 20 | ||||
| #define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 | #define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 | ||||
| #define SMAA_MAX_SEARCH_STEPS 362 /* 362 - 1 = 19^2 */ | #define SMAA_MAX_SEARCH_STEPS 362 /* 362 - 1 = 19^2 */ | ||||
| #define SMAA_MAX_SEARCH_STEPS_DIAG 19 | #define SMAA_MAX_SEARCH_STEPS_DIAG 19 | ||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| /* Internal Functions to Sample Pixel Color from Image */ | /* Internal Functions to Sample Pixel Color from Image */ | ||||
| /* TODO(manzanilla): to be removed with tiled implementation. Replace it with | |||||
| * #buffer->read_elem_checked. */ | |||||
| static inline void sample(SocketReader *reader, int x, int y, float color[4]) | static inline void sample(SocketReader *reader, int x, int y, float color[4]) | ||||
| { | { | ||||
| if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) { | if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) { | ||||
| color[0] = color[1] = color[2] = color[3] = 0.0; | color[0] = color[1] = color[2] = color[3] = 0.0; | ||||
| return; | return; | ||||
| } | } | ||||
| reader->read(color, x, y, nullptr); | reader->read(color, x, y, nullptr); | ||||
| } | } | ||||
| static void sample_bilinear_vertical( | static inline void sample(MemoryBuffer *reader, int x, int y, float color[4]) | ||||
| SocketReader *reader, int x, int y, float yoffset, float color[4]) | { | ||||
| reader->read_elem_checked(x, y, color); | |||||
| } | |||||
| template<typename T> | |||||
| static void sample_bilinear_vertical(T *reader, int x, int y, float yoffset, float color[4]) | |||||
| { | { | ||||
| float iy = floorf(yoffset); | float iy = floorf(yoffset); | ||||
| float fy = yoffset - iy; | float fy = yoffset - iy; | ||||
| y += (int)iy; | y += (int)iy; | ||||
| float color00[4], color01[4]; | float color00[4], color01[4]; | ||||
| sample(reader, x + 0, y + 0, color00); | sample(reader, x + 0, y + 0, color00); | ||||
| sample(reader, x + 0, y + 1, color01); | sample(reader, x + 0, y + 1, color01); | ||||
| color[0] = interpf(color01[0], color00[0], fy); | color[0] = interpf(color01[0], color00[0], fy); | ||||
| color[1] = interpf(color01[1], color00[1], fy); | color[1] = interpf(color01[1], color00[1], fy); | ||||
| color[2] = interpf(color01[2], color00[2], fy); | color[2] = interpf(color01[2], color00[2], fy); | ||||
| color[3] = interpf(color01[3], color00[3], fy); | color[3] = interpf(color01[3], color00[3], fy); | ||||
| } | } | ||||
| static void sample_bilinear_horizontal( | template<typename T> | ||||
| SocketReader *reader, int x, int y, float xoffset, float color[4]) | static void sample_bilinear_horizontal(T *reader, int x, int y, float xoffset, float color[4]) | ||||
| { | { | ||||
| float ix = floorf(xoffset); | float ix = floorf(xoffset); | ||||
| float fx = xoffset - ix; | float fx = xoffset - ix; | ||||
| x += (int)ix; | x += (int)ix; | ||||
| float color00[4], color10[4]; | float color00[4], color10[4]; | ||||
| sample(reader, x + 0, y + 0, color00); | sample(reader, x + 0, y + 0, color00); | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| /* Edge Detection (First Pass) */ | /* Edge Detection (First Pass) */ | ||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() | SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() | ||||
| { | { | ||||
| this->addInputSocket(DataType::Color); /* image */ | this->addInputSocket(DataType::Color); /* image */ | ||||
| this->addInputSocket(DataType::Value); /* depth, material ID, etc. */ | this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ | ||||
| this->addOutputSocket(DataType::Color); | this->addOutputSocket(DataType::Color); | ||||
| this->flags.complex = true; | this->flags.complex = true; | ||||
| this->m_imageReader = nullptr; | this->m_imageReader = nullptr; | ||||
| this->m_valueReader = nullptr; | this->m_valueReader = nullptr; | ||||
| this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); | this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); | ||||
| this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); | this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); | ||||
| } | } | ||||
| Show All 28 Lines | bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest( | ||||
| newInput.xmax = input->xmax + 1; | newInput.xmax = input->xmax + 1; | ||||
| newInput.xmin = input->xmin - 2; | newInput.xmin = input->xmin - 2; | ||||
| newInput.ymax = input->ymax + 1; | newInput.ymax = input->ymax + 1; | ||||
| newInput.ymin = input->ymin - 2; | newInput.ymin = input->ymin - 2; | ||||
| return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | ||||
| } | } | ||||
| void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx), | |||||
| const rcti &output_area, | |||||
| rcti &r_input_area) | |||||
| { | |||||
| r_input_area.xmax = output_area.xmax + 1; | |||||
| r_input_area.xmin = output_area.xmin - 2; | |||||
| r_input_area.ymax = output_area.ymax + 1; | |||||
| r_input_area.ymin = output_area.ymin - 2; | |||||
| } | |||||
| void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/) | void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/) | ||||
| { | { | ||||
| float color[4]; | float color[4]; | ||||
| /* Calculate luma deltas: */ | /* Calculate luma deltas: */ | ||||
| sample(m_imageReader, x, y, color); | sample(m_imageReader, x, y, color); | ||||
| float L = IMB_colormanagement_get_luminance(color); | float L = IMB_colormanagement_get_luminance(color); | ||||
| sample(m_imageReader, x - 1, y, color); | sample(m_imageReader, x - 1, y, color); | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | if (output[1] != 0.0f) { | ||||
| /* Local contrast adaptation: */ | /* Local contrast adaptation: */ | ||||
| if (maxDelta > m_contrast_limit * Dtop) { | if (maxDelta > m_contrast_limit * Dtop) { | ||||
| output[1] = 0.0f; | output[1] = 0.0f; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *output, | |||||
| const rcti &area, | |||||
| Span<MemoryBuffer *> inputs) | |||||
| { | |||||
| const MemoryBuffer *image = inputs[0]; | |||||
| for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { | |||||
| float color[4]; | |||||
| const int x = it.x; | |||||
| const int y = it.y; | |||||
| /* Calculate luma deltas: */ | |||||
| image->read_elem_checked(x, y, color); | |||||
| const float L = IMB_colormanagement_get_luminance(color); | |||||
| image->read_elem_checked(x - 1, y, color); | |||||
| const float Lleft = IMB_colormanagement_get_luminance(color); | |||||
| image->read_elem_checked(x, y - 1, color); | |||||
| const float Ltop = IMB_colormanagement_get_luminance(color); | |||||
| const float Dleft = fabsf(L - Lleft); | |||||
| const float Dtop = fabsf(L - Ltop); | |||||
| /* We do the usual threshold: */ | |||||
| it.out[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; | |||||
| it.out[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; | |||||
| it.out[2] = 0.0f; | |||||
| it.out[3] = 1.0f; | |||||
| /* Then discard if there is no edge: */ | |||||
| if (is_zero_v2(it.out)) { | |||||
| continue; | |||||
| } | |||||
| /* Calculate right and bottom deltas: */ | |||||
| image->read_elem_checked(x + 1, y, color); | |||||
| const float Lright = IMB_colormanagement_get_luminance(color); | |||||
| image->read_elem_checked(x, y + 1, color); | |||||
| const float Lbottom = IMB_colormanagement_get_luminance(color); | |||||
| const float Dright = fabsf(L - Lright); | |||||
| const float Dbottom = fabsf(L - Lbottom); | |||||
| /* Calculate the maximum delta in the direct neighborhood: */ | |||||
| float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); | |||||
| /* Calculate luma used for both left and top edges: */ | |||||
| image->read_elem_checked(x - 1, y - 1, color); | |||||
| const float Llefttop = IMB_colormanagement_get_luminance(color); | |||||
| /* Left edge */ | |||||
| if (it.out[0] != 0.0f) { | |||||
| /* Calculate deltas around the left pixel: */ | |||||
| image->read_elem_checked(x - 2, y, color); | |||||
| const float Lleftleft = IMB_colormanagement_get_luminance(color); | |||||
| image->read_elem_checked(x - 1, y + 1, color); | |||||
| const float Lleftbottom = IMB_colormanagement_get_luminance(color); | |||||
| const float Dleftleft = fabsf(Lleft - Lleftleft); | |||||
| const float Dlefttop = fabsf(Lleft - Llefttop); | |||||
| const float Dleftbottom = fabsf(Lleft - Lleftbottom); | |||||
| /* Calculate the final maximum delta: */ | |||||
| maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); | |||||
| /* Local contrast adaptation: */ | |||||
| if (maxDelta > m_contrast_limit * Dleft) { | |||||
| it.out[0] = 0.0f; | |||||
| } | |||||
| } | |||||
| /* Top edge */ | |||||
| if (it.out[1] != 0.0f) { | |||||
| /* Calculate top-top delta: */ | |||||
| image->read_elem_checked(x, y - 2, color); | |||||
| const float Ltoptop = IMB_colormanagement_get_luminance(color); | |||||
| image->read_elem_checked(x + 1, y - 1, color); | |||||
| const float Ltopright = IMB_colormanagement_get_luminance(color); | |||||
| const float Dtoptop = fabsf(Ltop - Ltoptop); | |||||
| const float Dtopleft = fabsf(Ltop - Llefttop); | |||||
| const float Dtopright = fabsf(Ltop - Ltopright); | |||||
| /* Calculate the final maximum delta: */ | |||||
| maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); | |||||
| /* Local contrast adaptation: */ | |||||
| if (maxDelta > m_contrast_limit * Dtop) { | |||||
| it.out[1] = 0.0f; | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| /* Blending Weight Calculation (Second Pass) */ | /* Blending Weight Calculation (Second Pass) */ | ||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() | SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() | ||||
| { | { | ||||
| this->addInputSocket(DataType::Color); /* edges */ | this->addInputSocket(DataType::Color); /* edges */ | ||||
| this->addOutputSocket(DataType::Color); | this->addOutputSocket(DataType::Color); | ||||
| this->flags.complex = true; | this->flags.complex = true; | ||||
| this->m_imageReader = nullptr; | this->m_imageReader = nullptr; | ||||
| this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); | this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); | ||||
| } | } | ||||
| void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) | void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) | ||||
| { | { | ||||
| return getInputOperation(0)->initializeTileData(rect); | return getInputOperation(0)->initializeTileData(rect); | ||||
| } | } | ||||
| void SMAABlendingWeightCalculationOperation::initExecution() | void SMAABlendingWeightCalculationOperation::initExecution() | ||||
| { | { | ||||
| this->m_imageReader = this->getInputSocketReader(0); | this->m_imageReader = this->getInputSocketReader(0); | ||||
| if (execution_model_ == eExecutionModel::Tiled) { | |||||
| sample_image_fn_ = [=](int x, int y, float *out) { sample(m_imageReader, x, y, out); }; | |||||
| } | |||||
| } | } | ||||
| void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) | void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) | ||||
| { | { | ||||
| /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */ | /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */ | ||||
| m_corner_rounding = static_cast<int>(scalenorm(0, 100, rounding)); | m_corner_rounding = static_cast<int>(scalenorm(0, 100, rounding)); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | if (edges[0] > 0.0f) { | ||||
| /* Fix corners: */ | /* Fix corners: */ | ||||
| if (m_corner_rounding) { | if (m_corner_rounding) { | ||||
| detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2); | detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| void SMAABlendingWeightCalculationOperation::update_memory_buffer_started( | |||||
| MemoryBuffer *UNUSED(output), const rcti &UNUSED(out_area), Span<MemoryBuffer *> inputs) | |||||
| { | |||||
| const MemoryBuffer *image = inputs[0]; | |||||
| sample_image_fn_ = [=](int x, int y, float *out) { image->read_elem_checked(x, y, out); }; | |||||
| } | |||||
| void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( | |||||
| MemoryBuffer *output, const rcti &out_area, Span<MemoryBuffer *> UNUSED(inputs)) | |||||
| { | |||||
| for (BuffersIterator<float> it = output->iterate_with({}, out_area); !it.is_end(); ++it) { | |||||
| const int x = it.x; | |||||
| const int y = it.y; | |||||
| zero_v4(it.out); | |||||
| float edges[4]; | |||||
| sample_image_fn_(x, y, edges); | |||||
| /* Edge at north */ | |||||
| float c[4]; | |||||
| if (edges[1] > 0.0f) { | |||||
| /* Diagonals have both north and west edges, so calculating weights for them */ | |||||
| /* in one of the boundaries is enough. */ | |||||
| calculateDiagWeights(x, y, edges, it.out); | |||||
| /* We give priority to diagonals, so if we find a diagonal we skip. */ | |||||
| /* horizontal/vertical processing. */ | |||||
| if (!is_zero_v2(it.out)) { | |||||
| continue; | |||||
| } | |||||
| /* Find the distance to the left and the right: */ | |||||
| int left = searchXLeft(x, y); | |||||
| int right = searchXRight(x, y); | |||||
| int d1 = x - left, d2 = right - x; | |||||
| /* Fetch the left and right crossing edges: */ | |||||
| int e1 = 0, e2 = 0; | |||||
| sample_image_fn_(left, y - 1, c); | |||||
| if (c[0] > 0.0) { | |||||
| e1 += 1; | |||||
| } | |||||
| sample_image_fn_(left, y, c); | |||||
| if (c[0] > 0.0) { | |||||
| e1 += 2; | |||||
| } | |||||
| sample_image_fn_(right + 1, y - 1, c); | |||||
| if (c[0] > 0.0) { | |||||
| e2 += 1; | |||||
| } | |||||
| sample_image_fn_(right + 1, y, c); | |||||
| if (c[0] > 0.0) { | |||||
| e2 += 2; | |||||
| } | |||||
| /* Ok, we know how this pattern looks like, now it is time for getting */ | |||||
| /* the actual area: */ | |||||
| area(d1, d2, e1, e2, it.out); /* R, G */ | |||||
| /* Fix corners: */ | |||||
| if (m_corner_rounding) { | |||||
| detectHorizontalCornerPattern(it.out, left, right, y, d1, d2); | |||||
| } | |||||
| } | |||||
| /* Edge at west */ | |||||
| if (edges[0] > 0.0f) { | |||||
| /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ | |||||
| if (isVerticalSearchUnneeded(x, y)) { | |||||
| continue; | |||||
| } | |||||
| /* Find the distance to the top and the bottom: */ | |||||
| int top = searchYUp(x, y); | |||||
| int bottom = searchYDown(x, y); | |||||
| int d1 = y - top, d2 = bottom - y; | |||||
| /* Fetch the top and bottom crossing edges: */ | |||||
| int e1 = 0, e2 = 0; | |||||
| sample_image_fn_(x - 1, top, c); | |||||
| if (c[1] > 0.0) { | |||||
| e1 += 1; | |||||
| } | |||||
| sample_image_fn_(x, top, c); | |||||
| if (c[1] > 0.0) { | |||||
| e1 += 2; | |||||
| } | |||||
| sample_image_fn_(x - 1, bottom + 1, c); | |||||
| if (c[1] > 0.0) { | |||||
| e2 += 1; | |||||
| } | |||||
| sample_image_fn_(x, bottom + 1, c); | |||||
| if (c[1] > 0.0) { | |||||
| e2 += 2; | |||||
| } | |||||
| /* Get the area for this direction: */ | |||||
| area(d1, d2, e1, e2, it.out + 2); /* B, A */ | |||||
| /* Fix corners: */ | |||||
| if (m_corner_rounding) { | |||||
| detectVerticalCornerPattern(it.out + 2, x, top, bottom, d1, d2); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| void SMAABlendingWeightCalculationOperation::deinitExecution() | void SMAABlendingWeightCalculationOperation::deinitExecution() | ||||
| { | { | ||||
| this->m_imageReader = nullptr; | this->m_imageReader = nullptr; | ||||
| } | } | ||||
| bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( | bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( | ||||
| rcti *input, ReadBufferOperation *readOperation, rcti *output) | rcti *input, ReadBufferOperation *readOperation, rcti *output) | ||||
| { | { | ||||
| rcti newInput; | rcti newInput; | ||||
| newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); | newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); | ||||
| newInput.xmin = input->xmin - | newInput.xmin = input->xmin - | ||||
| fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); | fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); | ||||
| newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); | newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); | ||||
| newInput.ymin = input->ymin - | newInput.ymin = input->ymin - | ||||
| fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); | fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); | ||||
| return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | ||||
| } | } | ||||
| void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUSED(input_idx), | |||||
| const rcti &output_area, | |||||
| rcti &r_input_area) | |||||
| { | |||||
| r_input_area.xmax = output_area.xmax + | |||||
| fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); | |||||
| r_input_area.xmin = output_area.xmin - | |||||
| fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); | |||||
| r_input_area.ymax = output_area.ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); | |||||
| r_input_area.ymin = output_area.ymin - | |||||
| fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); | |||||
| } | |||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| /* Diagonal Search Functions */ | /* Diagonal Search Functions */ | ||||
| /** | /** | ||||
| * These functions allows to perform diagonal pattern searches. | * These functions allows to perform diagonal pattern searches. | ||||
| */ | */ | ||||
| int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found) | int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found) | ||||
| { | { | ||||
| float e[4]; | float e[4]; | ||||
| int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; | int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; | ||||
| *found = false; | *found = false; | ||||
| while (x != end) { | while (x != end) { | ||||
| x += dir; | x += dir; | ||||
| y -= dir; | y -= dir; | ||||
| sample(m_imageReader, x, y, e); | sample_image_fn_(x, y, e); | ||||
| if (e[1] == 0.0f) { | if (e[1] == 0.0f) { | ||||
| *found = true; | *found = true; | ||||
| break; | break; | ||||
| } | } | ||||
| if (e[0] == 0.0f) { | if (e[0] == 0.0f) { | ||||
| *found = true; | *found = true; | ||||
| return (dir < 0) ? x : x - dir; | return (dir < 0) ? x : x - dir; | ||||
| } | } | ||||
| } | } | ||||
| return x - dir; | return x - dir; | ||||
| } | } | ||||
| int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found) | int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found) | ||||
| { | { | ||||
| float e[4]; | float e[4]; | ||||
| int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; | int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; | ||||
| *found = false; | *found = false; | ||||
| while (x != end) { | while (x != end) { | ||||
| x += dir; | x += dir; | ||||
| y += dir; | y += dir; | ||||
| sample(m_imageReader, x, y, e); | sample_image_fn_(x, y, e); | ||||
| if (e[1] == 0.0f) { | if (e[1] == 0.0f) { | ||||
| *found = true; | *found = true; | ||||
| break; | break; | ||||
| } | } | ||||
| sample(m_imageReader, x + 1, y, e); | sample_image_fn_(x + 1, y, e); | ||||
| if (e[0] == 0.0f) { | if (e[0] == 0.0f) { | ||||
| *found = true; | *found = true; | ||||
| return (dir > 0) ? x : x - dir; | return (dir > 0) ? x : x - dir; | ||||
| } | } | ||||
| } | } | ||||
| return x - dir; | return x - dir; | ||||
| } | } | ||||
| Show All 28 Lines | void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, | ||||
| if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */ | if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */ | ||||
| int e1 = 0, e2 = 0; | int e1 = 0, e2 = 0; | ||||
| if (d1_found) { | if (d1_found) { | ||||
| /* Fetch the crossing edges: */ | /* Fetch the crossing edges: */ | ||||
| int left = x - d1, bottom = y + d1; | int left = x - d1, bottom = y + d1; | ||||
| sample(m_imageReader, left - 1, bottom, c); | sample_image_fn_(left - 1, bottom, c); | ||||
| if (c[1] > 0.0) { | if (c[1] > 0.0) { | ||||
| e1 += 2; | e1 += 2; | ||||
| } | } | ||||
| sample(m_imageReader, left, bottom, c); | sample_image_fn_(left, bottom, c); | ||||
| if (c[0] > 0.0) { | if (c[0] > 0.0) { | ||||
| e1 += 1; | e1 += 1; | ||||
| } | } | ||||
| } | } | ||||
| if (d2_found) { | if (d2_found) { | ||||
| /* Fetch the crossing edges: */ | /* Fetch the crossing edges: */ | ||||
| int right = x + d2, top = y - d2; | int right = x + d2, top = y - d2; | ||||
| sample(m_imageReader, right + 1, top, c); | sample_image_fn_(right + 1, top, c); | ||||
| if (c[1] > 0.0) { | if (c[1] > 0.0) { | ||||
| e2 += 2; | e2 += 2; | ||||
| } | } | ||||
| sample(m_imageReader, right + 1, top - 1, c); | sample_image_fn_(right + 1, top - 1, c); | ||||
| if (c[0] > 0.0) { | if (c[0] > 0.0) { | ||||
| e2 += 1; | e2 += 1; | ||||
| } | } | ||||
| } | } | ||||
| /* Fetch the areas for this line: */ | /* Fetch the areas for this line: */ | ||||
| area_diag(d1, d2, e1, e2, weights); | area_diag(d1, d2, e1, e2, weights); | ||||
| } | } | ||||
| /* Search for the line ends: */ | /* Search for the line ends: */ | ||||
| d1 = x - searchDiag2(x, y, -1, &d1_found); | d1 = x - searchDiag2(x, y, -1, &d1_found); | ||||
| sample(m_imageReader, x + 1, y, e); | sample_image_fn_(x + 1, y, e); | ||||
| if (e[0] > 0.0f) { | if (e[0] > 0.0f) { | ||||
| d2 = searchDiag2(x, y, 1, &d2_found) - x; | d2 = searchDiag2(x, y, 1, &d2_found) - x; | ||||
| } | } | ||||
| else { | else { | ||||
| d2 = 0; | d2 = 0; | ||||
| d2_found = true; | d2_found = true; | ||||
| } | } | ||||
| if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */ | if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */ | ||||
| int e1 = 0, e2 = 0; | int e1 = 0, e2 = 0; | ||||
| if (d1_found) { | if (d1_found) { | ||||
| /* Fetch the crossing edges: */ | /* Fetch the crossing edges: */ | ||||
| int left = x - d1, top = y - d1; | int left = x - d1, top = y - d1; | ||||
| sample(m_imageReader, left - 1, top, c); | sample_image_fn_(left - 1, top, c); | ||||
| if (c[1] > 0.0) { | if (c[1] > 0.0) { | ||||
| e1 += 2; | e1 += 2; | ||||
| } | } | ||||
| sample(m_imageReader, left, top - 1, c); | sample_image_fn_(left, top - 1, c); | ||||
| if (c[0] > 0.0) { | if (c[0] > 0.0) { | ||||
| e1 += 1; | e1 += 1; | ||||
| } | } | ||||
| } | } | ||||
| if (d2_found) { | if (d2_found) { | ||||
| /* Fetch the crossing edges: */ | /* Fetch the crossing edges: */ | ||||
| int right = x + d2, bottom = y + d2; | int right = x + d2, bottom = y + d2; | ||||
| sample(m_imageReader, right + 1, bottom, c); | sample_image_fn_(right + 1, bottom, c); | ||||
| if (c[1] > 0.0) { | if (c[1] > 0.0) { | ||||
| e2 += 2; | e2 += 2; | ||||
| } | } | ||||
| if (c[0] > 0.0) { | if (c[0] > 0.0) { | ||||
| e2 += 1; | e2 += 1; | ||||
| } | } | ||||
| } | } | ||||
| Show All 11 Lines | bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int y) | ||||
| bool found; | bool found; | ||||
| float e[4]; | float e[4]; | ||||
| if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) { | if (SMAA_MAX_SEARCH_STEPS_DIAG <= 0) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* Search for the line ends: */ | /* Search for the line ends: */ | ||||
| sample(m_imageReader, x - 1, y, e); | sample_image_fn_(x - 1, y, e); | ||||
| if (e[1] > 0.0f) { | if (e[1] > 0.0f) { | ||||
| d1 = x - searchDiag2(x - 1, y, -1, &found); | d1 = x - searchDiag2(x - 1, y, -1, &found); | ||||
| } | } | ||||
| else { | else { | ||||
| d1 = 0; | d1 = 0; | ||||
| } | } | ||||
| d2 = searchDiag2(x - 1, y, 1, &found) - x; | d2 = searchDiag2(x - 1, y, 1, &found) - x; | ||||
| return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */ | return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */ | ||||
| } | } | ||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| /* Horizontal/Vertical Search Functions */ | /* Horizontal/Vertical Search Functions */ | ||||
| int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) | int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) | ||||
| { | { | ||||
| int end = x - SMAA_MAX_SEARCH_STEPS; | int end = x - SMAA_MAX_SEARCH_STEPS; | ||||
| float e[4]; | float e[4]; | ||||
| while (x > end) { | while (x > end) { | ||||
| sample(m_imageReader, x, y, e); | sample_image_fn_(x, y, e); | ||||
| if (e[1] == 0.0f) { /* Is the edge not activated? */ | if (e[1] == 0.0f) { /* Is the edge not activated? */ | ||||
| break; | break; | ||||
| } | } | ||||
| if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| return x; | return x; | ||||
| } | } | ||||
| sample(m_imageReader, x, y - 1, e); | sample_image_fn_(x, y - 1, e); | ||||
| if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| return x; | return x; | ||||
| } | } | ||||
| x--; | x--; | ||||
| } | } | ||||
| return x + 1; | return x + 1; | ||||
| } | } | ||||
| int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) | int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) | ||||
| { | { | ||||
| int end = x + SMAA_MAX_SEARCH_STEPS; | int end = x + SMAA_MAX_SEARCH_STEPS; | ||||
| float e[4]; | float e[4]; | ||||
| while (x < end) { | while (x < end) { | ||||
| x++; | x++; | ||||
| sample(m_imageReader, x, y, e); | sample_image_fn_(x, y, e); | ||||
| if (e[1] == 0.0f || /* Is the edge not activated? */ | if (e[1] == 0.0f || /* Is the edge not activated? */ | ||||
| e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| break; | break; | ||||
| } | } | ||||
| sample(m_imageReader, x, y - 1, e); | sample_image_fn_(x, y - 1, e); | ||||
| if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | if (e[0] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return x - 1; | return x - 1; | ||||
| } | } | ||||
| int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) | int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) | ||||
| { | { | ||||
| int end = y - SMAA_MAX_SEARCH_STEPS; | int end = y - SMAA_MAX_SEARCH_STEPS; | ||||
| float e[4]; | float e[4]; | ||||
| while (y > end) { | while (y > end) { | ||||
| sample(m_imageReader, x, y, e); | sample_image_fn_(x, y, e); | ||||
| if (e[0] == 0.0f) { /* Is the edge not activated? */ | if (e[0] == 0.0f) { /* Is the edge not activated? */ | ||||
| break; | break; | ||||
| } | } | ||||
| if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| return y; | return y; | ||||
| } | } | ||||
| sample(m_imageReader, x - 1, y, e); | sample_image_fn_(x - 1, y, e); | ||||
| if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| return y; | return y; | ||||
| } | } | ||||
| y--; | y--; | ||||
| } | } | ||||
| return y + 1; | return y + 1; | ||||
| } | } | ||||
| int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) | int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) | ||||
| { | { | ||||
| int end = y + SMAA_MAX_SEARCH_STEPS; | int end = y + SMAA_MAX_SEARCH_STEPS; | ||||
| float e[4]; | float e[4]; | ||||
| while (y < end) { | while (y < end) { | ||||
| y++; | y++; | ||||
| sample(m_imageReader, x, y, e); | sample_image_fn_(x, y, e); | ||||
| if (e[0] == 0.0f || /* Is the edge not activated? */ | if (e[0] == 0.0f || /* Is the edge not activated? */ | ||||
| e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| break; | break; | ||||
| } | } | ||||
| sample(m_imageReader, x - 1, y, e); | sample_image_fn_(x - 1, y, e); | ||||
| if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | if (e[1] != 0.0f) { /* Or is there a crossing edge that breaks the line? */ | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return y - 1; | return y - 1; | ||||
| } | } | ||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| /* Corner Detection Functions */ | /* Corner Detection Functions */ | ||||
| void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( | void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( | ||||
| float weights[2], int left, int right, int y, int d1, int d2) | float weights[2], int left, int right, int y, int d1, int d2) | ||||
| { | { | ||||
| float factor[2] = {1.0f, 1.0f}; | float factor[2] = {1.0f, 1.0f}; | ||||
| float rounding = m_corner_rounding / 100.0f; | float rounding = m_corner_rounding / 100.0f; | ||||
| float e[4]; | float e[4]; | ||||
| /* Reduce blending for pixels in the center of a line. */ | /* Reduce blending for pixels in the center of a line. */ | ||||
| rounding *= (d1 == d2) ? 0.5f : 1.0f; | rounding *= (d1 == d2) ? 0.5f : 1.0f; | ||||
| /* Near the left corner */ | /* Near the left corner */ | ||||
| if (d1 <= d2) { | if (d1 <= d2) { | ||||
| sample(m_imageReader, left, y + 1, e); | sample_image_fn_(left, y + 1, e); | ||||
| factor[0] -= rounding * e[0]; | factor[0] -= rounding * e[0]; | ||||
| sample(m_imageReader, left, y - 2, e); | sample_image_fn_(left, y - 2, e); | ||||
| factor[1] -= rounding * e[0]; | factor[1] -= rounding * e[0]; | ||||
| } | } | ||||
| /* Near the right corner */ | /* Near the right corner */ | ||||
| if (d1 >= d2) { | if (d1 >= d2) { | ||||
| sample(m_imageReader, right + 1, y + 1, e); | sample_image_fn_(right + 1, y + 1, e); | ||||
| factor[0] -= rounding * e[0]; | factor[0] -= rounding * e[0]; | ||||
| sample(m_imageReader, right + 1, y - 2, e); | sample_image_fn_(right + 1, y - 2, e); | ||||
| factor[1] -= rounding * e[0]; | factor[1] -= rounding * e[0]; | ||||
| } | } | ||||
| weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f); | weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f); | ||||
| weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f); | weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f); | ||||
| } | } | ||||
| void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( | void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( | ||||
| float weights[2], int x, int top, int bottom, int d1, int d2) | float weights[2], int x, int top, int bottom, int d1, int d2) | ||||
| { | { | ||||
| float factor[2] = {1.0f, 1.0f}; | float factor[2] = {1.0f, 1.0f}; | ||||
| float rounding = m_corner_rounding / 100.0f; | float rounding = m_corner_rounding / 100.0f; | ||||
| float e[4]; | float e[4]; | ||||
| /* Reduce blending for pixels in the center of a line. */ | /* Reduce blending for pixels in the center of a line. */ | ||||
| rounding *= (d1 == d2) ? 0.5f : 1.0f; | rounding *= (d1 == d2) ? 0.5f : 1.0f; | ||||
| /* Near the top corner */ | /* Near the top corner */ | ||||
| if (d1 <= d2) { | if (d1 <= d2) { | ||||
| sample(m_imageReader, x + 1, top, e); | sample_image_fn_(x + 1, top, e); | ||||
| factor[0] -= rounding * e[1]; | factor[0] -= rounding * e[1]; | ||||
| sample(m_imageReader, x - 2, top, e); | sample_image_fn_(x - 2, top, e); | ||||
| factor[1] -= rounding * e[1]; | factor[1] -= rounding * e[1]; | ||||
| } | } | ||||
| /* Near the bottom corner */ | /* Near the bottom corner */ | ||||
| if (d1 >= d2) { | if (d1 >= d2) { | ||||
| sample(m_imageReader, x + 1, bottom + 1, e); | sample_image_fn_(x + 1, bottom + 1, e); | ||||
| factor[0] -= rounding * e[1]; | factor[0] -= rounding * e[1]; | ||||
| sample(m_imageReader, x - 2, bottom + 1, e); | sample_image_fn_(x - 2, bottom + 1, e); | ||||
| factor[1] -= rounding * e[1]; | factor[1] -= rounding * e[1]; | ||||
| } | } | ||||
| weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f); | weights[0] *= CLAMPIS(factor[0], 0.0f, 1.0f); | ||||
| weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f); | weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f); | ||||
| } | } | ||||
| /*-----------------------------------------------------------------------------*/ | /*-----------------------------------------------------------------------------*/ | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | void SMAANeighborhoodBlendingOperation::executePixel(float output[4], | ||||
| /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ | /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ | ||||
| samplefunc(m_image1Reader, x, y, offset1, color1); | samplefunc(m_image1Reader, x, y, offset1, color1); | ||||
| samplefunc(m_image1Reader, x, y, offset2, color2); | samplefunc(m_image1Reader, x, y, offset2, color2); | ||||
| mul_v4_v4fl(output, color1, weight1); | mul_v4_v4fl(output, color1, weight1); | ||||
| madd_v4_v4fl(output, color2, weight2); | madd_v4_v4fl(output, color2, weight2); | ||||
| } | } | ||||
| void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffer *output, | |||||
| const rcti &out_area, | |||||
| Span<MemoryBuffer *> inputs) | |||||
| { | |||||
| MemoryBuffer *image1 = inputs[0]; | |||||
| MemoryBuffer *image2 = inputs[1]; | |||||
| for (BuffersIterator<float> it = output->iterate_with({}, out_area); !it.is_end(); ++it) { | |||||
| const float x = it.x; | |||||
| const float y = it.y; | |||||
| float w[4]; | |||||
| /* Fetch the blending weights for current pixel: */ | |||||
| image2->read_elem_checked(x, y, w); | |||||
| const float left = w[2], top = w[0]; | |||||
| image2->read_elem_checked(x + 1, y, w); | |||||
| const float right = w[3]; | |||||
| image2->read_elem_checked(x, y + 1, w); | |||||
| const float bottom = w[1]; | |||||
| /* Is there any blending weight with a value greater than 0.0? */ | |||||
| if (right + bottom + left + top < 1e-5f) { | |||||
| image1->read_elem_checked(x, y, it.out); | |||||
| continue; | |||||
| } | |||||
| /* Calculate the blending offsets: */ | |||||
| void (*sample_fn)(MemoryBuffer * reader, int x, int y, float xoffset, float color[4]); | |||||
| float offset1, offset2, weight1, weight2, color1[4], color2[4]; | |||||
| if (fmaxf(right, left) > fmaxf(bottom, top)) { /* `max(horizontal) > max(vertical)` */ | |||||
| sample_fn = sample_bilinear_horizontal; | |||||
| offset1 = right; | |||||
| offset2 = -left; | |||||
| weight1 = right / (right + left); | |||||
| weight2 = left / (right + left); | |||||
| } | |||||
| else { | |||||
| sample_fn = sample_bilinear_vertical; | |||||
| offset1 = bottom; | |||||
| offset2 = -top; | |||||
| weight1 = bottom / (bottom + top); | |||||
| weight2 = top / (bottom + top); | |||||
| } | |||||
| /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ | |||||
| sample_fn(image1, x, y, offset1, color1); | |||||
| sample_fn(image1, x, y, offset2, color2); | |||||
| mul_v4_v4fl(it.out, color1, weight1); | |||||
| madd_v4_v4fl(it.out, color2, weight2); | |||||
| } | |||||
| } | |||||
| void SMAANeighborhoodBlendingOperation::deinitExecution() | void SMAANeighborhoodBlendingOperation::deinitExecution() | ||||
| { | { | ||||
| this->m_image1Reader = nullptr; | this->m_image1Reader = nullptr; | ||||
| this->m_image2Reader = nullptr; | this->m_image2Reader = nullptr; | ||||
| } | } | ||||
| bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( | bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( | ||||
| rcti *input, ReadBufferOperation *readOperation, rcti *output) | rcti *input, ReadBufferOperation *readOperation, rcti *output) | ||||
| { | { | ||||
| rcti newInput; | rcti newInput; | ||||
| newInput.xmax = input->xmax + 1; | newInput.xmax = input->xmax + 1; | ||||
| newInput.xmin = input->xmin - 1; | newInput.xmin = input->xmin - 1; | ||||
| newInput.ymax = input->ymax + 1; | newInput.ymax = input->ymax + 1; | ||||
| newInput.ymin = input->ymin - 1; | newInput.ymin = input->ymin - 1; | ||||
| return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | ||||
| } | } | ||||
| void SMAANeighborhoodBlendingOperation::get_area_of_interest(const int UNUSED(input_idx), | |||||
| const rcti &output_area, | |||||
| rcti &r_input_area) | |||||
| { | |||||
| r_input_area = output_area; | |||||
| expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); | |||||
| } | |||||
| } // namespace blender::compositor | } // namespace blender::compositor | ||||