Changeset View
Changeset View
Standalone View
Standalone View
source/blender/compositor/operations/COM_RotateOperation.cc
| Show All 19 Lines | |||||
| #include "COM_ConstantOperation.h" | #include "COM_ConstantOperation.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| namespace blender::compositor { | namespace blender::compositor { | ||||
| RotateOperation::RotateOperation() | RotateOperation::RotateOperation() | ||||
| { | { | ||||
| this->addInputSocket(DataType::Color); | this->addInputSocket(DataType::Color, ResizeMode::None); | ||||
| this->addInputSocket(DataType::Value); | this->addInputSocket(DataType::Value, ResizeMode::None); | ||||
| this->addOutputSocket(DataType::Color); | this->addOutputSocket(DataType::Color); | ||||
| this->setResolutionInputSocketIndex(0); | this->set_canvas_input_index(0); | ||||
| this->m_imageSocket = nullptr; | this->m_imageSocket = nullptr; | ||||
| this->m_degreeSocket = nullptr; | this->m_degreeSocket = nullptr; | ||||
| this->m_doDegree2RadConversion = false; | this->m_doDegree2RadConversion = false; | ||||
| this->m_isDegreeSet = false; | this->m_isDegreeSet = false; | ||||
| sampler_ = PixelSampler::Bilinear; | sampler_ = PixelSampler::Bilinear; | ||||
| } | } | ||||
| void RotateOperation::get_rotation_center(const rcti &area, float &r_x, float &r_y) | |||||
| { | |||||
| r_x = (BLI_rcti_size_x(&area) - 1) / 2.0; | |||||
| r_y = (BLI_rcti_size_y(&area) - 1) / 2.0; | |||||
| } | |||||
| void RotateOperation::get_rotation_offset(const rcti &input_canvas, | |||||
| const rcti &rotate_canvas, | |||||
| float &r_offset_x, | |||||
| float &r_offset_y) | |||||
| { | |||||
| r_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&rotate_canvas)) / 2.0f; | |||||
| r_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&rotate_canvas)) / 2.0f; | |||||
| } | |||||
| void RotateOperation::get_area_rotation_bounds(const rcti &area, | void RotateOperation::get_area_rotation_bounds(const rcti &area, | ||||
| const float center_x, | const float center_x, | ||||
| const float center_y, | const float center_y, | ||||
| const float sine, | const float sine, | ||||
| const float cosine, | const float cosine, | ||||
| rcti &r_bounds) | rcti &r_bounds) | ||||
| { | { | ||||
| const float dxmin = area.xmin - center_x; | const float dxmin = area.xmin - center_x; | ||||
| const float dymin = area.ymin - center_y; | const float dymin = area.ymin - center_y; | ||||
| const float dxmax = area.xmax - center_x; | const float dxmax = area.xmax - center_x; | ||||
| const float dymax = area.ymax - center_y; | const float dymax = area.ymax - center_y; | ||||
| const float x1 = center_x + (cosine * dxmin + sine * dymin); | const float x1 = center_x + (cosine * dxmin + (-sine) * dymin); | ||||
| const float x2 = center_x + (cosine * dxmax + sine * dymin); | const float x2 = center_x + (cosine * dxmax + (-sine) * dymin); | ||||
| const float x3 = center_x + (cosine * dxmin + sine * dymax); | const float x3 = center_x + (cosine * dxmin + (-sine) * dymax); | ||||
| const float x4 = center_x + (cosine * dxmax + sine * dymax); | const float x4 = center_x + (cosine * dxmax + (-sine) * dymax); | ||||
| const float y1 = center_y + (-sine * dxmin + cosine * dymin); | const float y1 = center_y + (sine * dxmin + cosine * dymin); | ||||
| const float y2 = center_y + (-sine * dxmax + cosine * dymin); | const float y2 = center_y + (sine * dxmax + cosine * dymin); | ||||
| const float y3 = center_y + (-sine * dxmin + cosine * dymax); | const float y3 = center_y + (sine * dxmin + cosine * dymax); | ||||
| const float y4 = center_y + (-sine * dxmax + cosine * dymax); | const float y4 = center_y + (sine * dxmax + cosine * dymax); | ||||
| const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); | const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); | ||||
| const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); | const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); | ||||
| const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); | const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); | ||||
| const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4))); | const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4))); | ||||
| r_bounds.xmin = floor(minx); | r_bounds.xmin = floor(minx); | ||||
| r_bounds.xmax = ceil(maxx); | r_bounds.xmax = ceil(maxx); | ||||
| r_bounds.ymin = floor(miny); | r_bounds.ymin = floor(miny); | ||||
| r_bounds.ymax = ceil(maxy); | r_bounds.ymax = ceil(maxy); | ||||
| } | } | ||||
| void RotateOperation::get_area_rotation_bounds_inverted(const rcti &area, | |||||
| const float center_x, | |||||
| const float center_y, | |||||
| const float sine, | |||||
| const float cosine, | |||||
| rcti &r_bounds) | |||||
| { | |||||
| get_area_rotation_bounds(area, center_x, center_y, -sine, cosine, r_bounds); | |||||
| } | |||||
| void RotateOperation::get_rotation_area_of_interest(const rcti &input_canvas, | |||||
| const rcti &rotate_canvas, | |||||
| const float sine, | |||||
| const float cosine, | |||||
| const rcti &output_area, | |||||
| rcti &r_input_area) | |||||
| { | |||||
| float center_x, center_y; | |||||
| get_rotation_center(input_canvas, center_x, center_y); | |||||
| float rotate_offset_x, rotate_offset_y; | |||||
| get_rotation_offset(input_canvas, rotate_canvas, rotate_offset_x, rotate_offset_y); | |||||
| r_input_area = output_area; | |||||
| BLI_rcti_translate(&r_input_area, rotate_offset_x, rotate_offset_y); | |||||
| get_area_rotation_bounds_inverted(r_input_area, center_x, center_y, sine, cosine, r_input_area); | |||||
| } | |||||
| void RotateOperation::get_rotation_canvas(const rcti &input_canvas, | |||||
| const float sine, | |||||
| const float cosine, | |||||
| rcti &r_canvas) | |||||
| { | |||||
| float center_x, center_y; | |||||
| get_rotation_center(input_canvas, center_x, center_y); | |||||
| rcti rot_bounds; | |||||
| get_area_rotation_bounds(input_canvas, center_x, center_y, sine, cosine, rot_bounds); | |||||
| float offset_x, offset_y; | |||||
| get_rotation_offset(input_canvas, rot_bounds, offset_x, offset_y); | |||||
| r_canvas = rot_bounds; | |||||
| BLI_rcti_translate(&r_canvas, -offset_x, -offset_y); | |||||
| } | |||||
| void RotateOperation::init_data() | void RotateOperation::init_data() | ||||
| { | { | ||||
| this->m_centerX = (getWidth() - 1) / 2.0; | if (execution_model_ == eExecutionModel::Tiled) { | ||||
| this->m_centerY = (getHeight() - 1) / 2.0; | get_rotation_center(get_canvas(), m_centerX, m_centerY); | ||||
| } | |||||
| } | } | ||||
| void RotateOperation::initExecution() | void RotateOperation::initExecution() | ||||
| { | { | ||||
| this->m_imageSocket = this->getInputSocketReader(0); | this->m_imageSocket = this->getInputSocketReader(0); | ||||
| this->m_degreeSocket = this->getInputSocketReader(1); | this->m_degreeSocket = this->getInputSocketReader(1); | ||||
| } | } | ||||
| void RotateOperation::deinitExecution() | void RotateOperation::deinitExecution() | ||||
| { | { | ||||
| this->m_imageSocket = nullptr; | this->m_imageSocket = nullptr; | ||||
| this->m_degreeSocket = nullptr; | this->m_degreeSocket = nullptr; | ||||
| } | } | ||||
| inline void RotateOperation::ensureDegree() | inline void RotateOperation::ensureDegree() | ||||
| { | { | ||||
| if (!this->m_isDegreeSet) { | if (!this->m_isDegreeSet) { | ||||
| float degree[4]; | float degree[4]; | ||||
| switch (execution_model_) { | switch (execution_model_) { | ||||
| case eExecutionModel::Tiled: | case eExecutionModel::Tiled: | ||||
| this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); | this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); | ||||
| break; | break; | ||||
| case eExecutionModel::FullFrame: | case eExecutionModel::FullFrame: | ||||
| NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); | degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); | ||||
| const bool is_constant_degree = degree_op->get_flags().is_constant_operation; | |||||
| degree[0] = is_constant_degree ? | |||||
| static_cast<ConstantOperation *>(degree_op)->get_constant_elem()[0] : | |||||
| 0.0f; | |||||
| break; | break; | ||||
| } | } | ||||
| double rad; | double rad; | ||||
| if (this->m_doDegree2RadConversion) { | if (this->m_doDegree2RadConversion) { | ||||
| rad = DEG2RAD((double)degree[0]); | rad = DEG2RAD((double)degree[0]); | ||||
| } | } | ||||
| else { | else { | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | bool RotateOperation::determineDependingAreaOfInterest(rcti *input, | ||||
| newInput.xmax = ceil(maxx) + 1; | newInput.xmax = ceil(maxx) + 1; | ||||
| newInput.xmin = floor(minx) - 1; | newInput.xmin = floor(minx) - 1; | ||||
| newInput.ymax = ceil(maxy) + 1; | newInput.ymax = ceil(maxy) + 1; | ||||
| newInput.ymin = floor(miny) - 1; | newInput.ymin = floor(miny) - 1; | ||||
| return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); | ||||
| } | } | ||||
| void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) | |||||
| { | |||||
| if (execution_model_ == eExecutionModel::Tiled) { | |||||
| NodeOperation::determine_canvas(preferred_area, r_area); | |||||
| return; | |||||
| } | |||||
| const bool image_determined = | |||||
| getInputSocket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); | |||||
| if (image_determined) { | |||||
| rcti input_canvas = r_area; | |||||
| rcti unused; | |||||
| getInputSocket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused); | |||||
| ensureDegree(); | |||||
| get_rotation_canvas(input_canvas, m_sine, m_cosine, r_area); | |||||
| } | |||||
| } | |||||
| void RotateOperation::get_area_of_interest(const int input_idx, | void RotateOperation::get_area_of_interest(const int input_idx, | ||||
| const rcti &output_area, | const rcti &output_area, | ||||
| rcti &r_input_area) | rcti &r_input_area) | ||||
| { | { | ||||
| if (input_idx == DEGREE_INPUT_INDEX) { | if (input_idx == DEGREE_INPUT_INDEX) { | ||||
| /* Degrees input is always used as constant. */ | r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; | ||||
| r_input_area = COM_SINGLE_ELEM_AREA; | |||||
| return; | return; | ||||
| } | } | ||||
| ensureDegree(); | ensureDegree(); | ||||
| get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area); | |||||
| const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); | |||||
| get_rotation_area_of_interest( | |||||
| input_image_canvas, this->get_canvas(), m_sine, m_cosine, output_area, r_input_area); | |||||
| expand_area_for_sampler(r_input_area, sampler_); | expand_area_for_sampler(r_input_area, sampler_); | ||||
| } | } | ||||
| void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output, | void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output, | ||||
| const rcti &area, | const rcti &area, | ||||
| Span<MemoryBuffer *> inputs) | Span<MemoryBuffer *> inputs) | ||||
| { | { | ||||
| ensureDegree(); | |||||
| const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; | const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; | ||||
| NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); | |||||
| float center_x, center_y; | |||||
| get_rotation_center(image_op->get_canvas(), center_x, center_y); | |||||
| float rotate_offset_x, rotate_offset_y; | |||||
| get_rotation_offset( | |||||
| image_op->get_canvas(), this->get_canvas(), rotate_offset_x, rotate_offset_y); | |||||
| for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { | for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) { | ||||
| float x = it.x; | float x = rotate_offset_x + it.x + canvas_.xmin; | ||||
| float y = it.y; | float y = rotate_offset_y + it.y + canvas_.ymin; | ||||
| rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine); | rotate_coords(x, y, center_x, center_y, m_sine, m_cosine); | ||||
| input_img->read_elem_sampled(x, y, sampler_, it.out); | input_img->read_elem_sampled(x - canvas_.xmin, y - canvas_.ymin, sampler_, it.out); | ||||
| } | } | ||||
| } | } | ||||
| } // namespace blender::compositor | } // namespace blender::compositor | ||||