blob: 28fb4bdb10fd60b0fadc35914038a7b3de133cfd [file] [log] [blame]
Anthony Barbier6ff3b192017-09-04 18:44:23 +01001/*
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +00002 * Copyright (c) 2016-2018 ARM Limited.
Anthony Barbier6ff3b192017-09-04 18:44:23 +01003 *
4 * SPDX-License-Identifier: MIT
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24#include "arm_compute/core/NEON/kernels/NEChannelCombineKernel.h"
25
26#include "arm_compute/core/Error.h"
27#include "arm_compute/core/Helpers.h"
28#include "arm_compute/core/IAccessWindow.h"
29#include "arm_compute/core/IMultiImage.h"
30#include "arm_compute/core/ITensor.h"
31#include "arm_compute/core/MultiImageInfo.h"
32#include "arm_compute/core/TensorInfo.h"
33#include "arm_compute/core/Types.h"
34#include "arm_compute/core/Validate.h"
35#include "arm_compute/core/Window.h"
36
37#include <arm_neon.h>
38
39using namespace arm_compute;
40
41namespace arm_compute
42{
43class Coordinates;
44} // namespace arm_compute
45
46NEChannelCombineKernel::NEChannelCombineKernel()
47 : _func(nullptr), _planes{ { nullptr } }, _output(nullptr), _output_multi(nullptr), _x_subsampling{ { 1, 1, 1 } }, _y_subsampling{ { 1, 1, 1 } }, _num_elems_processed_per_iteration(8),
48_is_parallelizable(true)
49{
50}
51
52void NEChannelCombineKernel::configure(const ITensor *plane0, const ITensor *plane1, const ITensor *plane2, const ITensor *plane3, ITensor *output)
53{
54 ARM_COMPUTE_ERROR_ON_NULLPTR(plane0, plane1, plane2, output);
55 ARM_COMPUTE_ERROR_ON(plane0 == output);
56 ARM_COMPUTE_ERROR_ON(plane1 == output);
57 ARM_COMPUTE_ERROR_ON(plane2 == output);
58
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000059 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane0, Format::U8);
60 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane1, Format::U8);
61 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane2, Format::U8);
62 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(output, Format::RGB888, Format::RGBA8888, Format::UYVY422, Format::YUYV422);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010063
64 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane0, 1, DataType::U8);
65 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane1, 1, DataType::U8);
66 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane2, 1, DataType::U8);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010067
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000068 const Format output_format = output->info()->format();
69
70 // Check if horizontal dimension of Y plane is even and validate horizontal sub-sampling dimensions for U and V planes
71 if(Format::YUYV422 == output_format || Format::UYVY422 == output_format)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010072 {
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000073 // Validate Y plane of input and output
74 ARM_COMPUTE_ERROR_ON_TENSORS_NOT_EVEN(output_format, plane0, output);
75
76 // Validate U and V plane of the input
77 ARM_COMPUTE_ERROR_ON_TENSORS_NOT_SUBSAMPLED(output_format, plane0->info()->tensor_shape(), plane1, plane2);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010078 }
79
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000080 _planes[0] = plane0;
81 _planes[1] = plane1;
82 _planes[2] = plane2;
83 _planes[3] = nullptr;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010084
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000085 // Validate the last input tensor only for RGBA format
86 if(Format::RGBA8888 == output_format)
Anthony Barbier6ff3b192017-09-04 18:44:23 +010087 {
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000088 ARM_COMPUTE_ERROR_ON_NULLPTR(plane3);
89 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(plane3);
90
91 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane3, Format::U8);
Anthony Barbier6ff3b192017-09-04 18:44:23 +010092 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane3, 1, DataType::U8);
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +000093
94 _planes[3] = plane3;
Anthony Barbier6ff3b192017-09-04 18:44:23 +010095 }
96
Anthony Barbier6ff3b192017-09-04 18:44:23 +010097 _output = output;
98 _output_multi = nullptr;
99
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000100 // Half the processed elements for U and V channels due to horizontal sub-sampling of 2
101 if(Format::YUYV422 == output_format || Format::UYVY422 == output_format)
102 {
103 _x_subsampling[1] = 2;
104 _x_subsampling[2] = 2;
105 }
106
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100107 _num_elems_processed_per_iteration = 8;
108 _is_parallelizable = true;
109
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000110 // Select function and number of elements to process given the output format
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100111 switch(output_format)
112 {
113 case Format::RGB888:
114 _func = &NEChannelCombineKernel::combine_3C;
115 break;
116 case Format::RGBA8888:
117 _func = &NEChannelCombineKernel::combine_4C;
118 break;
119 case Format::UYVY422:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100120 _num_elems_processed_per_iteration = 16;
121 _func = &NEChannelCombineKernel::combine_YUV_1p<true>;
122 break;
123 case Format::YUYV422:
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100124 _num_elems_processed_per_iteration = 16;
125 _func = &NEChannelCombineKernel::combine_YUV_1p<false>;
126 break;
127 default:
128 ARM_COMPUTE_ERROR("Not supported format.");
129 break;
130 }
131
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100132 Window win = calculate_max_window(*plane0->info(), Steps(_num_elems_processed_per_iteration));
133
134 AccessWindowHorizontal output_access(output->info(), 0, _num_elems_processed_per_iteration);
135 AccessWindowHorizontal plane0_access(plane0->info(), 0, _num_elems_processed_per_iteration / _x_subsampling[1], 1.f / _x_subsampling[0]);
136 AccessWindowHorizontal plane1_access(plane1->info(), 0, _num_elems_processed_per_iteration / _x_subsampling[1], 1.f / _x_subsampling[1]);
137 AccessWindowHorizontal plane2_access(plane2->info(), 0, _num_elems_processed_per_iteration / _x_subsampling[1], 1.f / _x_subsampling[2]);
138 AccessWindowHorizontal plane3_access(plane3 == nullptr ? nullptr : plane3->info(), 0, _num_elems_processed_per_iteration);
139
140 update_window_and_padding(
141 win,
142 plane0_access,
143 plane1_access,
144 plane2_access,
145 plane3_access,
146 output_access);
147
148 ValidRegion valid_region = intersect_valid_regions(plane0->info()->valid_region(),
149 plane1->info()->valid_region(),
150 plane2->info()->valid_region());
151
152 if(plane3 != nullptr)
153 {
154 valid_region = intersect_valid_regions(plane3->info()->valid_region(), valid_region);
155 }
156
157 output_access.set_valid_region(win, ValidRegion(valid_region.anchor, output->info()->tensor_shape()));
158
159 INEKernel::configure(win);
160}
161
162void NEChannelCombineKernel::configure(const IImage *plane0, const IImage *plane1, const IImage *plane2, IMultiImage *output)
163{
164 ARM_COMPUTE_ERROR_ON_NULLPTR(plane0, plane1, plane2, output);
165 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(plane0);
166 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(plane1);
167 ARM_COMPUTE_ERROR_ON_TENSOR_NOT_2D(plane2);
168
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000169 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane0, Format::U8);
170 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane1, Format::U8);
171 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(plane2, Format::U8);
172 ARM_COMPUTE_ERROR_ON_FORMAT_NOT_IN(output, Format::NV12, Format::NV21, Format::IYUV, Format::YUV444);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100173
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100174 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane0, 1, DataType::U8);
175 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane1, 1, DataType::U8);
176 ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(plane2, 1, DataType::U8);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100177
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000178 const Format output_format = output->info()->format();
179
180 // Validate shape of Y plane to be even and shape of sub-sampling dimensions for U and V planes
181 // Perform validation only for formats which require sub-sampling.
182 if(Format::YUV444 != output_format)
183 {
184 // Validate Y plane of input and output
185 ARM_COMPUTE_ERROR_ON_TENSORS_NOT_EVEN(output_format, plane0, output->plane(0));
186
187 // Validate U and V plane of the input
188 ARM_COMPUTE_ERROR_ON_TENSORS_NOT_SUBSAMPLED(output_format, plane0->info()->tensor_shape(), plane1, plane2);
189
190 // Validate second plane U (NV12 and NV21 have a UV88 combined plane while IYUV has only the U plane)
191 // MultiImage generates the correct tensor shape but also check in case the tensor shape of planes was changed to a wrong size
192 ARM_COMPUTE_ERROR_ON_TENSORS_NOT_SUBSAMPLED(output_format, plane0->info()->tensor_shape(), output->plane(1));
193
194 // Validate the last plane V of format IYUV
195 if(Format::IYUV == output_format)
196 {
197 // Validate Y plane of the output
198 ARM_COMPUTE_ERROR_ON_TENSORS_NOT_SUBSAMPLED(output_format, plane0->info()->tensor_shape(), output->plane(2));
199 }
200 }
201
202 _planes[0] = plane0;
203 _planes[1] = plane1;
204 _planes[2] = plane2;
205 _planes[3] = nullptr;
206 _output = nullptr;
207 _output_multi = output;
208
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100209 bool has_two_planes = false;
210 unsigned int num_elems_written_plane1 = 8;
211
212 _num_elems_processed_per_iteration = 8;
213 _is_parallelizable = true;
214
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100215 switch(output_format)
216 {
217 case Format::NV12:
218 case Format::NV21:
219 _x_subsampling = { { 1, 2, 2 } };
220 _y_subsampling = { { 1, 2, 2 } };
221 _func = &NEChannelCombineKernel::combine_YUV_2p;
222 has_two_planes = true;
223 num_elems_written_plane1 = 16;
224 break;
225 case Format::IYUV:
226 _is_parallelizable = false;
227 _x_subsampling = { { 1, 2, 2 } };
228 _y_subsampling = { { 1, 2, 2 } };
229 _func = &NEChannelCombineKernel::combine_YUV_3p;
230 break;
231 case Format::YUV444:
232 _is_parallelizable = false;
233 _x_subsampling = { { 1, 1, 1 } };
234 _y_subsampling = { { 1, 1, 1 } };
235 _func = &NEChannelCombineKernel::combine_YUV_3p;
236 break;
237 default:
238 ARM_COMPUTE_ERROR("Not supported format.");
239 break;
240 }
241
242 const unsigned int y_step = *std::max_element(_y_subsampling.begin(), _y_subsampling.end());
243
244 Window win = calculate_max_window(*plane0->info(), Steps(_num_elems_processed_per_iteration, y_step));
245 AccessWindowRectangle output_plane0_access(output->plane(0)->info(), 0, 0, _num_elems_processed_per_iteration, 1, 1.f, 1.f / _y_subsampling[0]);
246 AccessWindowRectangle output_plane1_access(output->plane(1)->info(), 0, 0, num_elems_written_plane1, 1, 1.f / _x_subsampling[1], 1.f / _y_subsampling[1]);
247 AccessWindowRectangle output_plane2_access(has_two_planes ? nullptr : output->plane(2)->info(), 0, 0, _num_elems_processed_per_iteration, 1, 1.f / _x_subsampling[2], 1.f / _y_subsampling[2]);
248
249 update_window_and_padding(win,
250 AccessWindowHorizontal(plane0->info(), 0, _num_elems_processed_per_iteration),
251 AccessWindowRectangle(plane1->info(), 0, 0, _num_elems_processed_per_iteration, 1, 1.f / _x_subsampling[1], 1.f / _y_subsampling[1]),
252 AccessWindowRectangle(plane2->info(), 0, 0, _num_elems_processed_per_iteration, 1, 1.f / _x_subsampling[2], 1.f / _y_subsampling[2]),
253 output_plane0_access,
254 output_plane1_access,
255 output_plane2_access);
256
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000257 ValidRegion plane0_valid_region = plane0->info()->valid_region();
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100258 ValidRegion output_plane1_region = has_two_planes ? intersect_valid_regions(plane1->info()->valid_region(), plane2->info()->valid_region()) : plane2->info()->valid_region();
259
260 output_plane0_access.set_valid_region(win, ValidRegion(plane0_valid_region.anchor, output->plane(0)->info()->tensor_shape()));
261 output_plane1_access.set_valid_region(win, ValidRegion(output_plane1_region.anchor, output->plane(1)->info()->tensor_shape()));
262 output_plane2_access.set_valid_region(win, ValidRegion(plane2->info()->valid_region().anchor, output->plane(2)->info()->tensor_shape()));
263
264 INEKernel::configure(win);
265}
266
267bool NEChannelCombineKernel::is_parallelisable() const
268{
269 return _is_parallelizable;
270}
271
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100272void NEChannelCombineKernel::run(const Window &window, const ThreadInfo &info)
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100273{
Moritz Pflanzerc186b572017-09-07 09:48:04 +0100274 ARM_COMPUTE_UNUSED(info);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100275 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
276 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
277 ARM_COMPUTE_ERROR_ON(_func == nullptr);
278
279 (this->*_func)(window);
280}
281
282void NEChannelCombineKernel::combine_3C(const Window &win)
283{
284 Iterator p0(_planes[0], win);
285 Iterator p1(_planes[1], win);
286 Iterator p2(_planes[2], win);
287 Iterator out(_output, win);
288
289 execute_window_loop(win, [&](const Coordinates & id)
290 {
291 const auto p0_ptr = static_cast<uint8_t *>(p0.ptr());
292 const auto p1_ptr = static_cast<uint8_t *>(p1.ptr());
293 const auto p2_ptr = static_cast<uint8_t *>(p2.ptr());
294 const auto out_ptr = static_cast<uint8_t *>(out.ptr());
295
296 const uint8x8x3_t pixels =
297 {
298 {
299 vld1_u8(p0_ptr),
300 vld1_u8(p1_ptr),
301 vld1_u8(p2_ptr)
302 }
303 };
304
305 vst3_u8(out_ptr, pixels);
306 },
307 p0, p1, p2, out);
308}
309
310void NEChannelCombineKernel::combine_4C(const Window &win)
311{
312 Iterator p0(_planes[0], win);
313 Iterator p1(_planes[1], win);
314 Iterator p2(_planes[2], win);
315 Iterator p3(_planes[3], win);
316 Iterator out(_output, win);
317
318 execute_window_loop(win, [&](const Coordinates & id)
319 {
320 const auto p0_ptr = static_cast<uint8_t *>(p0.ptr());
321 const auto p1_ptr = static_cast<uint8_t *>(p1.ptr());
322 const auto p2_ptr = static_cast<uint8_t *>(p2.ptr());
323 const auto p3_ptr = static_cast<uint8_t *>(p3.ptr());
324 const auto out_ptr = static_cast<uint8_t *>(out.ptr());
325
326 const uint8x8x4_t pixels =
327 {
328 {
329 vld1_u8(p0_ptr),
330 vld1_u8(p1_ptr),
331 vld1_u8(p2_ptr),
332 vld1_u8(p3_ptr)
333 }
334 };
335
336 vst4_u8(out_ptr, pixels);
337 },
338 p0, p1, p2, p3, out);
339}
340
341template <bool is_uyvy>
342void NEChannelCombineKernel::combine_YUV_1p(const Window &win)
343{
344 // Create sub-sampled uv window and init uv planes
345 Window win_uv(win);
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000346 win_uv.set_dimension_step(Window::DimX, win.x().step() / _x_subsampling[1]);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100347 win_uv.validate();
348
349 Iterator p0(_planes[0], win);
350 Iterator p1(_planes[1], win_uv);
351 Iterator p2(_planes[2], win_uv);
352 Iterator out(_output, win);
353
354 constexpr auto shift = is_uyvy ? 1 : 0;
355
356 execute_window_loop(win, [&](const Coordinates & id)
357 {
358 const auto p0_ptr = static_cast<uint8_t *>(p0.ptr());
359 const auto p1_ptr = static_cast<uint8_t *>(p1.ptr());
360 const auto p2_ptr = static_cast<uint8_t *>(p2.ptr());
361 const auto out_ptr = static_cast<uint8_t *>(out.ptr());
362
363 const uint8x8x2_t pixels_y = vld2_u8(p0_ptr);
364 const uint8x8x2_t pixels_uv =
365 {
366 {
367 vld1_u8(p1_ptr),
368 vld1_u8(p2_ptr)
369 }
370 };
371
372 uint8x8x4_t pixels{ {} };
373 pixels.val[0 + shift] = pixels_y.val[0];
374 pixels.val[1 - shift] = pixels_uv.val[0];
375 pixels.val[2 + shift] = pixels_y.val[1];
376 pixels.val[3 - shift] = pixels_uv.val[1];
377
378 vst4_u8(out_ptr, pixels);
379 },
380 p0, p1, p2, out);
381}
382
383void NEChannelCombineKernel::combine_YUV_2p(const Window &win)
384{
385 ARM_COMPUTE_ERROR_ON(win.x().start() % _x_subsampling[1]);
386 ARM_COMPUTE_ERROR_ON(win.y().start() % _y_subsampling[1]);
387
388 // Copy first plane
389 copy_plane(win, 0);
390
391 // Update UV window
392 Window uv_win(win);
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000393 uv_win.set(Window::DimX, Window::Dimension(uv_win.x().start() / _x_subsampling[1], uv_win.x().end() / _x_subsampling[1], uv_win.x().step() / _x_subsampling[1]));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100394 uv_win.set(Window::DimY, Window::Dimension(uv_win.y().start() / _y_subsampling[1], uv_win.y().end() / _y_subsampling[1], 1));
395 uv_win.validate();
396
397 // Update output win
398 Window out_win(win);
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000399 out_win.set(Window::DimX, Window::Dimension(out_win.x().start(), out_win.x().end(), out_win.x().step() / _x_subsampling[1]));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100400 out_win.set(Window::DimY, Window::Dimension(out_win.y().start() / _y_subsampling[1], out_win.y().end() / _y_subsampling[1], 1));
401 out_win.validate();
402
403 // Construct second plane
404 const int shift = (Format::NV12 == _output_multi->info()->format()) ? 0 : 1;
405 Iterator p1(_planes[1 + shift], uv_win);
406 Iterator p2(_planes[2 - shift], uv_win);
407 Iterator out(_output_multi->plane(1), out_win);
408
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000409 // Increase step size after iterator is created to calculate stride correctly for multi channel format
410 out_win.set_dimension_step(Window::DimX, out_win.x().step() * _x_subsampling[1]);
411
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100412 execute_window_loop(out_win, [&](const Coordinates & id)
413 {
414 const uint8x8x2_t pixels =
415 {
416 {
417 vld1_u8(p1.ptr()),
418 vld1_u8(p2.ptr())
419 }
420 };
421
422 vst2_u8(out.ptr(), pixels);
423 },
424 p1, p2, out);
425}
426
427void NEChannelCombineKernel::combine_YUV_3p(const Window &win)
428{
429 copy_plane(win, 0);
430 copy_plane(win, 1);
431 copy_plane(win, 2);
432}
433
434void NEChannelCombineKernel::copy_plane(const Window &win, uint32_t plane_id)
435{
436 ARM_COMPUTE_ERROR_ON(win.x().start() % _x_subsampling[plane_id]);
437 ARM_COMPUTE_ERROR_ON(win.y().start() % _y_subsampling[plane_id]);
438
439 // Update window
440 Window tmp_win(win);
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000441 tmp_win.set(Window::DimX, Window::Dimension(tmp_win.x().start() / _x_subsampling[plane_id], tmp_win.x().end() / _x_subsampling[plane_id], tmp_win.x().step() / _x_subsampling[plane_id]));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100442 tmp_win.set(Window::DimY, Window::Dimension(tmp_win.y().start() / _y_subsampling[plane_id], tmp_win.y().end() / _y_subsampling[plane_id], 1));
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100443
444 Iterator in(_planes[plane_id], tmp_win);
445 Iterator out(_output_multi->plane(plane_id), tmp_win);
446
447 execute_window_loop(tmp_win, [&](const Coordinates & id)
448 {
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000449 const uint8x8_t pixels = vld1_u8(in.ptr());
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100450
Ioan-Cristian Szaboae3c8ab2017-11-16 17:55:03 +0000451 vst1_u8(out.ptr(), pixels);
Anthony Barbier6ff3b192017-09-04 18:44:23 +0100452 },
453 in, out);
454}