/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2011 Blender Foundation. */ #include "COM_MovieDistortionOperation.h" #include "DNA_defaults.h" #include "BKE_movieclip.h" namespace blender::compositor { MovieDistortionOperation::MovieDistortionOperation(bool distortion) { this->add_input_socket(DataType::Color); this->add_output_socket(DataType::Color); this->set_canvas_input_index(0); input_operation_ = nullptr; movie_clip_ = nullptr; apply_ = distortion; } void MovieDistortionOperation::init_data() { if (movie_clip_) { MovieTracking *tracking = &movie_clip_->tracking; MovieClipUser clip_user = *DNA_struct_default_get(MovieClipUser); int calibration_width, calibration_height; BKE_movieclip_user_set_frame(&clip_user, framenumber_); BKE_movieclip_get_size(movie_clip_, &clip_user, &calibration_width, &calibration_height); float delta[2]; rcti full_frame; full_frame.xmin = full_frame.ymin = 0; full_frame.xmax = this->get_width(); full_frame.ymax = this->get_height(); BKE_tracking_max_distortion_delta_across_bound( tracking, this->get_width(), this->get_height(), &full_frame, !apply_, delta); /* 5 is just in case we didn't hit real max of distortion in * BKE_tracking_max_undistortion_delta_across_bound */ margin_[0] = delta[0] + 5; margin_[1] = delta[1] + 5; calibration_width_ = calibration_width; calibration_height_ = calibration_height; pixel_aspect_ = tracking->camera.pixel_aspect; } else { margin_[0] = margin_[1] = 0; } } void MovieDistortionOperation::init_execution() { input_operation_ = this->get_input_socket_reader(0); if (movie_clip_) { MovieTracking *tracking = &movie_clip_->tracking; distortion_ = BKE_tracking_distortion_new(tracking, calibration_width_, calibration_height_); } else { distortion_ = nullptr; } } void MovieDistortionOperation::deinit_execution() { input_operation_ = nullptr; movie_clip_ = nullptr; if (distortion_ != nullptr) { BKE_tracking_distortion_free(distortion_); } } void MovieDistortionOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler /*sampler*/) { if (distortion_ != nullptr) { /* float overscan = 0.0f; */ const float pixel_aspect = pixel_aspect_; const float w = float(this->get_width()) /* / (1 + overscan) */; const float h = float(this->get_height()) /* / (1 + overscan) */; const float aspx = w / float(calibration_width_); const float aspy = h / float(calibration_height_); float in[2]; float out[2]; in[0] = (x /* - 0.5 * overscan * w */) / aspx; in[1] = (y /* - 0.5 * overscan * h */) / aspy / pixel_aspect; if (apply_) { BKE_tracking_distortion_undistort_v2(distortion_, in, out); } else { BKE_tracking_distortion_distort_v2(distortion_, in, out); } float u = out[0] * aspx /* + 0.5 * overscan * w */, v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect; input_operation_->read_sampled(output, u, v, PixelSampler::Bilinear); } else { input_operation_->read_sampled(output, x, y, PixelSampler::Bilinear); } } bool MovieDistortionOperation::determine_depending_area_of_interest( rcti *input, ReadBufferOperation *read_operation, rcti *output) { rcti new_input; new_input.xmin = input->xmin - margin_[0]; new_input.ymin = input->ymin - margin_[1]; new_input.xmax = input->xmax + margin_[0]; new_input.ymax = input->ymax + margin_[1]; return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void MovieDistortionOperation::get_area_of_interest(const int input_idx, const rcti &output_area, rcti &r_input_area) { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); r_input_area.xmin = output_area.xmin - margin_[0]; r_input_area.ymin = output_area.ymin - margin_[1]; r_input_area.xmax = output_area.xmax + margin_[0]; r_input_area.ymax = output_area.ymax + margin_[1]; } void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { const MemoryBuffer *input_img = inputs[0]; if (distortion_ == nullptr) { output->copy_from(input_img, area); return; } /* `float overscan = 0.0f;` */ const float pixel_aspect = pixel_aspect_; const float w = float(this->get_width()) /* `/ (1 + overscan)` */; const float h = float(this->get_height()) /* `/ (1 + overscan)` */; const float aspx = w / float(calibration_width_); const float aspy = h / float(calibration_height_); float xy[2]; float distorted_xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx; xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect; if (apply_) { BKE_tracking_distortion_undistort_v2(distortion_, xy, distorted_xy); } else { BKE_tracking_distortion_distort_v2(distortion_, xy, distorted_xy); } const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */; const float v = (distorted_xy[1] * aspy /* `+ 0.5 * overscan * h` */) * pixel_aspect; input_img->read_elem_bilinear(u, v, it.out); } } } // namespace blender::compositor