49#if defined(WITH_FFTW3)
54 const int kernel_size = (1 << settings->size) + 1;
59 const int needed_padding_amount = kernel_size / 2;
60 const int2 image_size =
int2(image->get_width(), image->get_height());
61 const int2 needed_spatial_size = image_size + needed_padding_amount;
68 const int2 frequency_size =
int2(spatial_size.x / 2 + 1, spatial_size.y);
70 float *kernel_spatial_domain = fftwf_alloc_real(spatial_size.x * spatial_size.y);
71 std::complex<float> *kernel_frequency_domain =
reinterpret_cast<std::complex<float> *
>(
72 fftwf_alloc_complex(frequency_size.x * frequency_size.y));
76 fftwf_plan forward_plan = fftwf_plan_dft_r2c_2d(
79 kernel_spatial_domain,
80 reinterpret_cast<fftwf_complex *
>(kernel_frequency_domain),
88 double &sum = sum_by_thread.local();
89 for (const int64_t y : sub_y_range) {
90 for (const int64_t x : IndexRange(spatial_size.x)) {
94 const int half_kernel_size = kernel_size / 2;
95 int64_t output_x = mod_i(x - half_kernel_size, spatial_size.x);
96 int64_t output_y = mod_i(y - half_kernel_size, spatial_size.y);
98 const bool is_inside_kernel = x < kernel_size && y < kernel_size;
99 if (is_inside_kernel) {
100 const float kernel_value = compute_fog_glow_kernel_value(x, y, kernel_size);
101 kernel_spatial_domain[output_x + output_y * spatial_size.x] = kernel_value;
105 kernel_spatial_domain[output_x + output_y * spatial_size.x] = 0.0f;
113 const float kernel_normalization_factor =
float(
114 std::accumulate(sum_by_thread.begin(), sum_by_thread.end(), 0.0));
116 fftwf_execute_dft_r2c(forward_plan,
117 kernel_spatial_domain,
118 reinterpret_cast<fftwf_complex *
>(kernel_frequency_domain));
119 fftwf_free(kernel_spatial_domain);
122 const int channels_count = 3;
123 const int64_t spatial_pixels_per_channel =
int64_t(spatial_size.x) * spatial_size.y;
124 const int64_t frequency_pixels_per_channel =
int64_t(frequency_size.x) * frequency_size.y;
125 const int64_t spatial_pixels_count = spatial_pixels_per_channel * channels_count;
126 const int64_t frequency_pixels_count = frequency_pixels_per_channel * channels_count;
128 float *image_spatial_domain = fftwf_alloc_real(spatial_pixels_count);
129 std::complex<float> *image_frequency_domain =
reinterpret_cast<std::complex<float> *
>(
130 fftwf_alloc_complex(frequency_pixels_count));
135 for (const int64_t y : sub_y_range) {
136 for (const int64_t x : IndexRange(spatial_size.x)) {
137 const bool is_inside_image = x < image_size.x && y < image_size.y;
138 for (const int64_t channel : IndexRange(channels_count)) {
139 const int64_t base_index = x + y * spatial_size.x;
140 const int64_t output_index = base_index + spatial_pixels_per_channel * channel;
141 if (is_inside_image) {
142 image_spatial_domain[output_index] = image->get_elem(x, y)[channel];
145 image_spatial_domain[output_index] = 0.0f;
153 for (
const int64_t channel : sub_range) {
154 fftwf_execute_dft_r2c(forward_plan,
155 image_spatial_domain + spatial_pixels_per_channel * channel,
156 reinterpret_cast<fftwf_complex *
>(image_frequency_domain) +
157 frequency_pixels_per_channel * channel);
166 const float normalization_scale =
float(spatial_size.x) * spatial_size.y *
167 kernel_normalization_factor;
169 for (const int64_t channel : IndexRange(channels_count)) {
170 for (const int64_t y : sub_y_range) {
171 for (const int64_t x : IndexRange(frequency_size.x)) {
172 const int64_t base_index = x + y * frequency_size.x;
173 const int64_t output_index = base_index + frequency_pixels_per_channel * channel;
174 const std::complex<float> kernel_value = kernel_frequency_domain[base_index];
175 image_frequency_domain[output_index] *= kernel_value / normalization_scale;
182 fftwf_plan backward_plan = fftwf_plan_dft_c2r_2d(
185 reinterpret_cast<fftwf_complex *
>(image_frequency_domain),
186 image_spatial_domain,
190 for (
const int64_t channel : sub_range) {
191 fftwf_execute_dft_c2r(backward_plan,
192 reinterpret_cast<fftwf_complex *
>(image_frequency_domain) +
193 frequency_pixels_per_channel * channel,
194 image_spatial_domain + spatial_pixels_per_channel * channel);
200 for (const int64_t y : sub_y_range) {
201 for (const int64_t x : IndexRange(image_size.x)) {
202 for (const int64_t channel : IndexRange(channels_count)) {
203 const int64_t output_index = (x + y * image_size.x) * image->get_num_channels();
204 const int64_t base_index = x + y * spatial_size.x;
205 const int64_t input_index = base_index + spatial_pixels_per_channel * channel;
206 output[output_index + channel] = image_spatial_domain[input_index];
207 output[output_index + 3] = image->get_buffer()[output_index + 3];
213 fftwf_destroy_plan(forward_plan);
214 fftwf_destroy_plan(backward_plan);
215 fftwf_free(image_spatial_domain);
216 fftwf_free(image_frequency_domain);
217 fftwf_free(kernel_frequency_domain);