1 /*
2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <cstring>
18 #include <cstdint>
19 #include <cmath>
20
21 #include "ultrahdr/editorhelper.h"
22
23 namespace ultrahdr {
24
25 template <typename T>
rotate_buffer_clockwise(T * src_buffer,T * dst_buffer,int src_w,int src_h,int src_stride,int dst_stride,int degree)26 void rotate_buffer_clockwise(T* src_buffer, T* dst_buffer, int src_w, int src_h, int src_stride,
27 int dst_stride, int degree) {
28 if (degree == 90) {
29 int dst_w = src_h;
30 int dst_h = src_w;
31 for (int i = 0; i < dst_h; i++) {
32 for (int j = 0; j < dst_w; j++) {
33 dst_buffer[i * dst_stride + j] = src_buffer[(src_h - j - 1) * src_stride + i];
34 }
35 }
36 } else if (degree == 180) {
37 int dst_w = src_w;
38 int dst_h = src_h;
39 for (int i = 0; i < dst_h; i++) {
40 for (int j = 0; j < dst_w; j++) {
41 dst_buffer[i * dst_stride + j] = src_buffer[(src_h - i - 1) * src_stride + (src_w - j - 1)];
42 }
43 }
44 } else if (degree == 270) {
45 int dst_w = src_h;
46 int dst_h = src_w;
47 for (int i = 0; i < dst_h; i++) {
48 for (int j = 0; j < dst_w; j++) {
49 dst_buffer[i * dst_stride + j] = src_buffer[j * src_stride + (src_w - i - 1)];
50 }
51 }
52 }
53 }
54
55 template <typename T>
mirror_buffer(T * src_buffer,T * dst_buffer,int src_w,int src_h,int src_stride,int dst_stride,uhdr_mirror_direction_t direction)56 void mirror_buffer(T* src_buffer, T* dst_buffer, int src_w, int src_h, int src_stride,
57 int dst_stride, uhdr_mirror_direction_t direction) {
58 if (direction == UHDR_MIRROR_VERTICAL) {
59 for (int i = 0; i < src_h; i++) {
60 memcpy(&dst_buffer[(src_h - i - 1) * dst_stride], &src_buffer[i * src_stride],
61 src_w * sizeof(T));
62 }
63 } else if (direction == UHDR_MIRROR_HORIZONTAL) {
64 for (int i = 0; i < src_h; i++) {
65 for (int j = 0; j < src_w; j++) {
66 dst_buffer[i * dst_stride + j] = src_buffer[i * src_stride + (src_w - j - 1)];
67 }
68 }
69 }
70 }
71
72 // TODO (dichenzhang): legacy method, need to be removed
73 template <typename T>
resize_buffer(T * src_buffer,T * dst_buffer,int src_w,int src_h,int dst_w,int dst_h,int src_stride,int dst_stride)74 void resize_buffer(T* src_buffer, T* dst_buffer, int src_w, int src_h, int dst_w, int dst_h,
75 int src_stride, int dst_stride) {
76 for (int i = 0; i < dst_h; i++) {
77 for (int j = 0; j < dst_w; j++) {
78 dst_buffer[i * dst_stride + j] =
79 src_buffer[i * (src_h / dst_h) * src_stride + j * (src_w / dst_w)];
80 }
81 }
82 }
83
84 // This function performs bicubic interpolation on a 1D signal.
bicubic_interpolate(double p0,double p1,double p2,double p3,double x)85 double bicubic_interpolate(double p0, double p1, double p2, double p3, double x) {
86 // Calculate the weights for the four neighboring points.
87 double w0 = (1 - x) * (1 - x) * (1 - x);
88 double w1 = 3 * x * (1 - x) * (1 - x);
89 double w2 = 3 * x * x * (1 - x);
90 double w3 = x * x * x;
91
92 // Calculate the interpolated value.
93 return w0 * p0 + w1 * p1 + w2 * p2 + w3 * p3;
94 }
95
96 template <typename T>
resize_buffer(T * src_buffer,T * dst_buffer,int src_w,int src_h,int dst_w,int dst_h,int src_stride,int dst_stride,uhdr_img_fmt_t img_fmt,size_t plane)97 void resize_buffer(T* src_buffer, T* dst_buffer, int src_w, int src_h, int dst_w, int dst_h,
98 int src_stride, int dst_stride, uhdr_img_fmt_t img_fmt, size_t plane) {
99 double scale_x = (double)src_w / dst_w;
100 double scale_y = (double)src_h / dst_h;
101 for (int y = 0; y < dst_h; y++) {
102 for (int x = 0; x < dst_w; x++) {
103 double ori_x = x * scale_x;
104 double ori_y = y * scale_y;
105 int p0_x = (int)floor(ori_x);
106 int p0_y = (int)floor(ori_y);
107 int p1_x = p0_x + 1;
108 int p1_y = p0_y;
109 int p2_x = p0_x;
110 int p2_y = p0_y + 1;
111 int p3_x = p0_x + 1;
112 int p3_y = p0_y + 1;
113
114 if ((img_fmt == UHDR_IMG_FMT_8bppYCbCr400) ||
115 (img_fmt == UHDR_IMG_FMT_12bppYCbCr420 && plane == UHDR_PLANE_Y) ||
116 (img_fmt == UHDR_IMG_FMT_12bppYCbCr420 && plane == UHDR_PLANE_U) ||
117 (img_fmt == UHDR_IMG_FMT_12bppYCbCr420 && plane == UHDR_PLANE_V)) {
118 double p0 = (double)src_buffer[p0_y * src_stride + p0_x];
119 double p1 = (double)src_buffer[p1_y * src_stride + p1_x];
120 double p2 = (double)src_buffer[p2_y * src_stride + p2_x];
121 double p3 = (double)src_buffer[p3_y * src_stride + p3_x];
122
123 double new_pix_val = bicubic_interpolate(p0, p1, p2, p3, ori_x - p0_x);
124
125 dst_buffer[y * dst_stride + x] = (uint8_t)floor(new_pix_val + 0.5);
126 } else {
127 // Unsupported feature.
128 return;
129 }
130 }
131 }
132 }
133
134 template void mirror_buffer<uint8_t>(uint8_t*, uint8_t*, int, int, int, int,
135 uhdr_mirror_direction_t);
136 template void mirror_buffer<uint16_t>(uint16_t*, uint16_t*, int, int, int, int,
137 uhdr_mirror_direction_t);
138 template void mirror_buffer<uint32_t>(uint32_t*, uint32_t*, int, int, int, int,
139 uhdr_mirror_direction_t);
140 template void mirror_buffer<uint64_t>(uint64_t*, uint64_t*, int, int, int, int,
141 uhdr_mirror_direction_t);
142
143 template void rotate_buffer_clockwise<uint8_t>(uint8_t*, uint8_t*, int, int, int, int, int);
144 template void rotate_buffer_clockwise<uint16_t>(uint16_t*, uint16_t*, int, int, int, int, int);
145 template void rotate_buffer_clockwise<uint32_t>(uint32_t*, uint32_t*, int, int, int, int, int);
146 template void rotate_buffer_clockwise<uint64_t>(uint64_t*, uint64_t*, int, int, int, int, int);
147
148 template void resize_buffer<uint8_t>(uint8_t*, uint8_t*, int, int, int, int, int, int);
149 template void resize_buffer<uint16_t>(uint16_t*, uint16_t*, int, int, int, int, int, int);
150 template void resize_buffer<uint32_t>(uint32_t*, uint32_t*, int, int, int, int, int, int);
151 template void resize_buffer<uint64_t>(uint64_t*, uint64_t*, int, int, int, int, int, int);
152
uhdr_mirror_effect(uhdr_mirror_direction_t direction)153 uhdr_mirror_effect::uhdr_mirror_effect(uhdr_mirror_direction_t direction) : m_direction{direction} {
154 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
155 m_mirror_uint8_t = mirror_buffer_neon<uint8_t>;
156 m_mirror_uint16_t = mirror_buffer_neon<uint16_t>;
157 m_mirror_uint32_t = mirror_buffer_neon<uint32_t>;
158 m_mirror_uint64_t = mirror_buffer_neon<uint64_t>;
159 #else
160 m_mirror_uint8_t = mirror_buffer<uint8_t>;
161 m_mirror_uint16_t = mirror_buffer<uint16_t>;
162 m_mirror_uint32_t = mirror_buffer<uint32_t>;
163 m_mirror_uint64_t = mirror_buffer<uint64_t>;
164 #endif
165 }
166
uhdr_rotate_effect(int degree)167 uhdr_rotate_effect::uhdr_rotate_effect(int degree) : m_degree{degree} {
168 #if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
169 m_rotate_uint8_t = rotate_buffer_clockwise_neon<uint8_t>;
170 m_rotate_uint16_t = rotate_buffer_clockwise_neon<uint16_t>;
171 m_rotate_uint32_t = rotate_buffer_clockwise_neon<uint32_t>;
172 m_rotate_uint64_t = rotate_buffer_clockwise_neon<uint64_t>;
173 #else
174 m_rotate_uint8_t = rotate_buffer_clockwise<uint8_t>;
175 m_rotate_uint16_t = rotate_buffer_clockwise<uint16_t>;
176 m_rotate_uint32_t = rotate_buffer_clockwise<uint32_t>;
177 m_rotate_uint64_t = rotate_buffer_clockwise<uint64_t>;
178 #endif
179 }
180
uhdr_resize_effect(int width,int height)181 uhdr_resize_effect::uhdr_resize_effect(int width, int height) : m_width{width}, m_height{height} {
182 m_resize_uint8_t = resize_buffer<uint8_t>;
183 m_resize_uint16_t = resize_buffer<uint16_t>;
184 m_resize_uint32_t = resize_buffer<uint32_t>;
185 m_resize_uint64_t = resize_buffer<uint64_t>;
186 }
187
apply_rotate(ultrahdr::uhdr_rotate_effect_t * desc,uhdr_raw_image_t * src)188 std::unique_ptr<uhdr_raw_image_ext_t> apply_rotate(ultrahdr::uhdr_rotate_effect_t* desc,
189 uhdr_raw_image_t* src) {
190 std::unique_ptr<uhdr_raw_image_ext_t> dst;
191
192 if (desc->m_degree == 90 || desc->m_degree == 270) {
193 dst = std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, src->h,
194 src->w, 64);
195 } else if (desc->m_degree == 180) {
196 dst = std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, src->w,
197 src->h, 64);
198 } else {
199 return nullptr;
200 }
201
202 if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
203 uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]);
204 uint16_t* dst_buffer = static_cast<uint16_t*>(dst->planes[UHDR_PLANE_Y]);
205 desc->m_rotate_uint16_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y],
206 dst->stride[UHDR_PLANE_Y], desc->m_degree);
207 uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]);
208 uint32_t* dst_uv_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_UV]);
209 desc->m_rotate_uint32_t(src_uv_buffer, dst_uv_buffer, src->w / 2, src->h / 2,
210 src->stride[UHDR_PLANE_UV] / 2, dst->stride[UHDR_PLANE_UV] / 2,
211 desc->m_degree);
212 } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) {
213 uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]);
214 uint8_t* dst_buffer = static_cast<uint8_t*>(dst->planes[UHDR_PLANE_Y]);
215 desc->m_rotate_uint8_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y],
216 dst->stride[UHDR_PLANE_Y], desc->m_degree);
217 if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
218 for (int i = 1; i < 3; i++) {
219 src_buffer = static_cast<uint8_t*>(src->planes[i]);
220 dst_buffer = static_cast<uint8_t*>(dst->planes[i]);
221 desc->m_rotate_uint8_t(src_buffer, dst_buffer, src->w / 2, src->h / 2, src->stride[i],
222 dst->stride[i], desc->m_degree);
223 }
224 }
225 } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
226 uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]);
227 uint32_t* dst_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_PACKED]);
228 desc->m_rotate_uint32_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED],
229 dst->stride[UHDR_PLANE_PACKED], desc->m_degree);
230 } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
231 uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]);
232 uint64_t* dst_buffer = static_cast<uint64_t*>(dst->planes[UHDR_PLANE_PACKED]);
233 desc->m_rotate_uint64_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED],
234 dst->stride[UHDR_PLANE_PACKED], desc->m_degree);
235 }
236 return dst;
237 }
238
apply_mirror(ultrahdr::uhdr_mirror_effect_t * desc,uhdr_raw_image_t * src)239 std::unique_ptr<uhdr_raw_image_ext_t> apply_mirror(ultrahdr::uhdr_mirror_effect_t* desc,
240 uhdr_raw_image_t* src) {
241 std::unique_ptr<uhdr_raw_image_ext_t> dst = std::make_unique<uhdr_raw_image_ext_t>(
242 src->fmt, src->cg, src->ct, src->range, src->w, src->h, 64);
243
244 if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
245 uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]);
246 uint16_t* dst_buffer = static_cast<uint16_t*>(dst->planes[UHDR_PLANE_Y]);
247 desc->m_mirror_uint16_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y],
248 dst->stride[UHDR_PLANE_Y], desc->m_direction);
249 uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]);
250 uint32_t* dst_uv_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_UV]);
251 desc->m_mirror_uint32_t(src_uv_buffer, dst_uv_buffer, src->w / 2, src->h / 2,
252 src->stride[UHDR_PLANE_UV] / 2, dst->stride[UHDR_PLANE_UV] / 2,
253 desc->m_direction);
254 } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) {
255 uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]);
256 uint8_t* dst_buffer = static_cast<uint8_t*>(dst->planes[UHDR_PLANE_Y]);
257 desc->m_mirror_uint8_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y],
258 dst->stride[UHDR_PLANE_Y], desc->m_direction);
259 if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
260 for (int i = 1; i < 3; i++) {
261 src_buffer = static_cast<uint8_t*>(src->planes[i]);
262 dst_buffer = static_cast<uint8_t*>(dst->planes[i]);
263 desc->m_mirror_uint8_t(src_buffer, dst_buffer, src->w / 2, src->h / 2, src->stride[i],
264 dst->stride[i], desc->m_direction);
265 }
266 }
267 } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
268 uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]);
269 uint32_t* dst_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_PACKED]);
270 desc->m_mirror_uint32_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED],
271 dst->stride[UHDR_PLANE_PACKED], desc->m_direction);
272 } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
273 uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]);
274 uint64_t* dst_buffer = static_cast<uint64_t*>(dst->planes[UHDR_PLANE_PACKED]);
275 desc->m_mirror_uint64_t(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED],
276 dst->stride[UHDR_PLANE_PACKED], desc->m_direction);
277 }
278 return dst;
279 }
280
apply_crop(uhdr_raw_image_t * src,int left,int top,int wd,int ht)281 void apply_crop(uhdr_raw_image_t* src, int left, int top, int wd, int ht) {
282 if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
283 uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]);
284 src->planes[UHDR_PLANE_Y] = &src_buffer[top * src->stride[UHDR_PLANE_Y] + left];
285 uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]);
286 src->planes[UHDR_PLANE_UV] =
287 &src_uv_buffer[(top / 2) * (src->stride[UHDR_PLANE_UV] / 2) + (left / 2)];
288 } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) {
289 uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]);
290 src->planes[UHDR_PLANE_Y] = &src_buffer[top * src->stride[UHDR_PLANE_Y] + left];
291 if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
292 for (int i = 1; i < 3; i++) {
293 src_buffer = static_cast<uint8_t*>(src->planes[i]);
294 src->planes[i] = &src_buffer[(top / 2) * src->stride[i] + (left / 2)];
295 }
296 }
297 } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
298 uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]);
299 src->planes[UHDR_PLANE_PACKED] = &src_buffer[top * src->stride[UHDR_PLANE_PACKED] + left];
300 } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
301 uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]);
302 src->planes[UHDR_PLANE_PACKED] = &src_buffer[top * src->stride[UHDR_PLANE_PACKED] + left];
303 }
304 src->w = wd;
305 src->h = ht;
306 }
307
apply_resize(ultrahdr::uhdr_resize_effect_t * desc,uhdr_raw_image_t * src,int dst_w,int dst_h)308 std::unique_ptr<uhdr_raw_image_ext_t> apply_resize(ultrahdr::uhdr_resize_effect_t* desc,
309 uhdr_raw_image_t* src, int dst_w, int dst_h) {
310 std::unique_ptr<uhdr_raw_image_ext_t> dst = std::make_unique<uhdr_raw_image_ext_t>(
311 src->fmt, src->cg, src->ct, src->range, dst_w, dst_h, 64);
312
313 if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
314 uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]);
315 uint16_t* dst_buffer = static_cast<uint16_t*>(dst->planes[UHDR_PLANE_Y]);
316 desc->m_resize_uint16_t(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h,
317 src->stride[UHDR_PLANE_Y], dst->stride[UHDR_PLANE_Y]);
318 uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]);
319 uint32_t* dst_uv_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_UV]);
320 desc->m_resize_uint32_t(src_uv_buffer, dst_uv_buffer, src->w / 2, src->h / 2, dst->w / 2,
321 dst->h / 2, src->stride[UHDR_PLANE_UV] / 2,
322 dst->stride[UHDR_PLANE_UV] / 2);
323 } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) {
324 uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]);
325 uint8_t* dst_buffer = static_cast<uint8_t*>(dst->planes[UHDR_PLANE_Y]);
326 desc->m_resize_uint8_t(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h,
327 src->stride[UHDR_PLANE_Y], dst->stride[UHDR_PLANE_Y]);
328 if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
329 for (int i = 1; i < 3; i++) {
330 src_buffer = static_cast<uint8_t*>(src->planes[i]);
331 dst_buffer = static_cast<uint8_t*>(dst->planes[i]);
332 desc->m_resize_uint8_t(src_buffer, dst_buffer, src->w / 2, src->h / 2, dst->w / 2,
333 dst->h / 2, src->stride[i], dst->stride[i]);
334 }
335 }
336 } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
337 uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]);
338 uint32_t* dst_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_PACKED]);
339 desc->m_resize_uint32_t(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h,
340 src->stride[UHDR_PLANE_PACKED], dst->stride[UHDR_PLANE_PACKED]);
341 } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) {
342 uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]);
343 uint64_t* dst_buffer = static_cast<uint64_t*>(dst->planes[UHDR_PLANE_PACKED]);
344 desc->m_resize_uint64_t(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h,
345 src->stride[UHDR_PLANE_PACKED], dst->stride[UHDR_PLANE_PACKED]);
346 }
347 return dst;
348 }
349
350 } // namespace ultrahdr
351