/src/libavif/ext/libyuv/source/scale_uv.cc
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2020 The LibYuv Project Authors. All rights reserved. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license |
5 | | * that can be found in the LICENSE file in the root of the source |
6 | | * tree. An additional intellectual property rights grant can be found |
7 | | * in the file PATENTS. All contributing project authors may |
8 | | * be found in the AUTHORS file in the root of the source tree. |
9 | | */ |
10 | | |
11 | | #include "libyuv/scale_uv.h" |
12 | | |
13 | | #include <assert.h> |
14 | | #include <limits.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include "libyuv/cpu_id.h" |
18 | | #include "libyuv/planar_functions.h" // For CopyUV |
19 | | #include "libyuv/row.h" |
20 | | #include "libyuv/scale_row.h" |
21 | | |
22 | | #ifdef __cplusplus |
23 | | namespace libyuv { |
24 | | extern "C" { |
25 | | #endif |
26 | | |
27 | | // Macros to enable specialized scalers |
28 | | |
29 | | #ifndef HAS_SCALEUVDOWN2 |
30 | | #define HAS_SCALEUVDOWN2 1 |
31 | | #endif |
32 | | #ifndef HAS_SCALEUVDOWN4BOX |
33 | | #define HAS_SCALEUVDOWN4BOX 1 |
34 | | #endif |
35 | | #ifndef HAS_SCALEUVDOWNEVEN |
36 | | #define HAS_SCALEUVDOWNEVEN 1 |
37 | | #endif |
38 | | #ifndef HAS_SCALEUVBILINEARDOWN |
39 | | #define HAS_SCALEUVBILINEARDOWN 1 |
40 | | #endif |
41 | | #ifndef HAS_SCALEUVBILINEARUP |
42 | | #define HAS_SCALEUVBILINEARUP 1 |
43 | | #endif |
44 | | #ifndef HAS_UVCOPY |
45 | | #define HAS_UVCOPY 1 |
46 | | #endif |
47 | | #ifndef HAS_SCALEPLANEVERTICAL |
48 | | #define HAS_SCALEPLANEVERTICAL 1 |
49 | | #endif |
50 | | |
51 | 0 | static __inline int Abs(int v) { |
52 | 0 | return v >= 0 ? v : -v; |
53 | 0 | } |
54 | | |
55 | | // ScaleUV, 1/2 |
56 | | // This is an optimized version for scaling down a UV to 1/2 of |
57 | | // its original size. |
58 | | #if HAS_SCALEUVDOWN2 |
59 | | static void ScaleUVDown2(int src_width, |
60 | | int src_height, |
61 | | int dst_width, |
62 | | int dst_height, |
63 | | ptrdiff_t src_stride, |
64 | | ptrdiff_t dst_stride, |
65 | | const uint8_t* src_uv, |
66 | | uint8_t* dst_uv, |
67 | | int x, |
68 | | int dx, |
69 | | int y, |
70 | | int dy, |
71 | 0 | enum FilterMode filtering) { |
72 | 0 | int j; |
73 | 0 | ptrdiff_t row_stride = src_stride * (dy >> 16); |
74 | 0 | void (*ScaleUVRowDown2)(const uint8_t* src_uv, ptrdiff_t src_stride, |
75 | 0 | uint8_t* dst_uv, int dst_width) = |
76 | 0 | filtering == kFilterNone |
77 | 0 | ? ScaleUVRowDown2_C |
78 | 0 | : (filtering == kFilterLinear ? ScaleUVRowDown2Linear_C |
79 | 0 | : ScaleUVRowDown2Box_C); |
80 | 0 | (void)src_width; |
81 | 0 | (void)src_height; |
82 | 0 | (void)dx; |
83 | 0 | assert(dx == 65536 * 2); // Test scale factor of 2. |
84 | 0 | assert((dy & 0x1ffff) == 0); // Test vertical scale is multiple of 2. |
85 | | // Advance to odd row, even column. |
86 | 0 | if (filtering == kFilterBilinear) { |
87 | 0 | src_uv += (y >> 16) * src_stride + (x >> 16) * 2; |
88 | 0 | } else { |
89 | 0 | src_uv += (y >> 16) * src_stride + ((x >> 16) - 1) * 2; |
90 | 0 | } |
91 | |
|
92 | 0 | #if defined(HAS_SCALEUVROWDOWN2BOX_SSSE3) |
93 | 0 | if (TestCpuFlag(kCpuHasSSSE3) && filtering) { |
94 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_Any_SSSE3; |
95 | 0 | if (IS_ALIGNED(dst_width, 4)) { |
96 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_SSSE3; |
97 | 0 | } |
98 | 0 | } |
99 | 0 | #endif |
100 | 0 | #if defined(HAS_SCALEUVROWDOWN2BOX_AVX2) |
101 | 0 | if (TestCpuFlag(kCpuHasAVX2) && filtering) { |
102 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_Any_AVX2; |
103 | 0 | if (IS_ALIGNED(dst_width, 8)) { |
104 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_AVX2; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | #endif |
108 | | #if defined(HAS_SCALEUVROWDOWN2_NEON) |
109 | | if (TestCpuFlag(kCpuHasNEON)) { |
110 | | ScaleUVRowDown2 = |
111 | | filtering == kFilterNone |
112 | | ? ScaleUVRowDown2_Any_NEON |
113 | | : (filtering == kFilterLinear ? ScaleUVRowDown2Linear_Any_NEON |
114 | | : ScaleUVRowDown2Box_Any_NEON); |
115 | | if (IS_ALIGNED(dst_width, 8)) { |
116 | | ScaleUVRowDown2 = |
117 | | filtering == kFilterNone |
118 | | ? ScaleUVRowDown2_NEON |
119 | | : (filtering == kFilterLinear ? ScaleUVRowDown2Linear_NEON |
120 | | : ScaleUVRowDown2Box_NEON); |
121 | | } |
122 | | } |
123 | | #endif |
124 | | #if defined(HAS_SCALEUVROWDOWN2_SME) |
125 | | if (TestCpuFlag(kCpuHasSME)) { |
126 | | ScaleUVRowDown2 = filtering == kFilterNone ? ScaleUVRowDown2_SME |
127 | | : filtering == kFilterLinear ? ScaleUVRowDown2Linear_SME |
128 | | : ScaleUVRowDown2Box_SME; |
129 | | } |
130 | | #endif |
131 | | #if defined(HAS_SCALEUVROWDOWN2_RVV) |
132 | | if (TestCpuFlag(kCpuHasRVV)) { |
133 | | ScaleUVRowDown2 = |
134 | | filtering == kFilterNone |
135 | | ? ScaleUVRowDown2_RVV |
136 | | : (filtering == kFilterLinear ? ScaleUVRowDown2Linear_RVV |
137 | | : ScaleUVRowDown2Box_RVV); |
138 | | } |
139 | | #endif |
140 | | |
141 | | // This code is not enabled. Only box filter is available at this time. |
142 | | #if defined(HAS_SCALEUVROWDOWN2_SSSE3) |
143 | | if (TestCpuFlag(kCpuHasSSSE3)) { |
144 | | ScaleUVRowDown2 = |
145 | | filtering == kFilterNone |
146 | | ? ScaleUVRowDown2_Any_SSSE3 |
147 | | : (filtering == kFilterLinear ? ScaleUVRowDown2Linear_Any_SSSE3 |
148 | | : ScaleUVRowDown2Box_Any_SSSE3); |
149 | | if (IS_ALIGNED(dst_width, 2)) { |
150 | | ScaleUVRowDown2 = |
151 | | filtering == kFilterNone |
152 | | ? ScaleUVRowDown2_SSSE3 |
153 | | : (filtering == kFilterLinear ? ScaleUVRowDown2Linear_SSSE3 |
154 | | : ScaleUVRowDown2Box_SSSE3); |
155 | | } |
156 | | } |
157 | | #endif |
158 | |
|
159 | 0 | if (filtering == kFilterLinear) { |
160 | 0 | src_stride = 0; |
161 | 0 | } |
162 | 0 | for (j = 0; j < dst_height; ++j) { |
163 | 0 | ScaleUVRowDown2(src_uv, src_stride, dst_uv, dst_width); |
164 | 0 | src_uv += row_stride; |
165 | 0 | dst_uv += dst_stride; |
166 | 0 | } |
167 | 0 | } |
168 | | #endif // HAS_SCALEUVDOWN2 |
169 | | |
170 | | // ScaleUV, 1/4 |
171 | | // This is an optimized version for scaling down a UV to 1/4 of |
172 | | // its original size. |
173 | | #if HAS_SCALEUVDOWN4BOX |
174 | | static int ScaleUVDown4Box(int src_width, |
175 | | int src_height, |
176 | | int dst_width, |
177 | | int dst_height, |
178 | | ptrdiff_t src_stride, |
179 | | ptrdiff_t dst_stride, |
180 | | const uint8_t* src_uv, |
181 | | uint8_t* dst_uv, |
182 | | int x, |
183 | | int dx, |
184 | | int y, |
185 | 0 | int dy) { |
186 | 0 | int j; |
187 | | // Allocate 2 rows of UV. |
188 | 0 | const int row_size = (dst_width * 2 * 2 + 15) & ~15; |
189 | 0 | align_buffer_64(row, row_size * 2); |
190 | 0 | if (!row) |
191 | 0 | return 1; |
192 | 0 | ptrdiff_t row_stride = src_stride * (dy >> 16); |
193 | 0 | void (*ScaleUVRowDown2)(const uint8_t* src_uv, ptrdiff_t src_stride, |
194 | 0 | uint8_t* dst_uv, int dst_width) = |
195 | 0 | ScaleUVRowDown2Box_C; |
196 | | // Advance to odd row, even column. |
197 | 0 | src_uv += (y >> 16) * src_stride + (x >> 16) * 2; |
198 | 0 | (void)src_width; |
199 | 0 | (void)src_height; |
200 | 0 | (void)dx; |
201 | 0 | assert(dx == 65536 * 4); // Test scale factor of 4. |
202 | 0 | assert((dy & 0x3ffff) == 0); // Test vertical scale is multiple of 4. |
203 | |
|
204 | 0 | #if defined(HAS_SCALEUVROWDOWN2BOX_SSSE3) |
205 | 0 | if (TestCpuFlag(kCpuHasSSSE3)) { |
206 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_Any_SSSE3; |
207 | 0 | if (IS_ALIGNED(dst_width, 4)) { |
208 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_SSSE3; |
209 | 0 | } |
210 | 0 | } |
211 | 0 | #endif |
212 | 0 | #if defined(HAS_SCALEUVROWDOWN2BOX_AVX2) |
213 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
214 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_Any_AVX2; |
215 | 0 | if (IS_ALIGNED(dst_width, 8)) { |
216 | 0 | ScaleUVRowDown2 = ScaleUVRowDown2Box_AVX2; |
217 | 0 | } |
218 | 0 | } |
219 | 0 | #endif |
220 | | #if defined(HAS_SCALEUVROWDOWN2BOX_NEON) |
221 | | if (TestCpuFlag(kCpuHasNEON)) { |
222 | | ScaleUVRowDown2 = ScaleUVRowDown2Box_Any_NEON; |
223 | | if (IS_ALIGNED(dst_width, 8)) { |
224 | | ScaleUVRowDown2 = ScaleUVRowDown2Box_NEON; |
225 | | } |
226 | | } |
227 | | #endif |
228 | | #if defined(HAS_SCALEUVROWDOWN2BOX_SME) |
229 | | if (TestCpuFlag(kCpuHasSME)) { |
230 | | ScaleUVRowDown2 = ScaleUVRowDown2Box_SME; |
231 | | } |
232 | | #endif |
233 | | #if defined(HAS_SCALEUVROWDOWN2BOX_RVV) |
234 | | if (TestCpuFlag(kCpuHasRVV)) { |
235 | | ScaleUVRowDown2 = ScaleUVRowDown2Box_RVV; |
236 | | } |
237 | | #endif |
238 | |
|
239 | 0 | for (j = 0; j < dst_height; ++j) { |
240 | 0 | ScaleUVRowDown2(src_uv, src_stride, row, dst_width * 2); |
241 | 0 | ScaleUVRowDown2(src_uv + src_stride * 2, src_stride, row + row_size, |
242 | 0 | dst_width * 2); |
243 | 0 | ScaleUVRowDown2(row, row_size, dst_uv, dst_width); |
244 | 0 | src_uv += row_stride; |
245 | 0 | dst_uv += dst_stride; |
246 | 0 | } |
247 | 0 | free_aligned_buffer_64(row); |
248 | 0 | return 0; |
249 | 0 | } |
250 | | #endif // HAS_SCALEUVDOWN4BOX |
251 | | |
252 | | // ScaleUV Even |
253 | | // This is an optimized version for scaling down a UV to even |
254 | | // multiple of its original size. |
255 | | #if HAS_SCALEUVDOWNEVEN |
256 | | static void ScaleUVDownEven(int src_width, |
257 | | int src_height, |
258 | | int dst_width, |
259 | | int dst_height, |
260 | | ptrdiff_t src_stride, |
261 | | ptrdiff_t dst_stride, |
262 | | const uint8_t* src_uv, |
263 | | uint8_t* dst_uv, |
264 | | int x, |
265 | | int dx, |
266 | | int y, |
267 | | int dy, |
268 | 0 | enum FilterMode filtering) { |
269 | 0 | int j; |
270 | 0 | int col_step = dx >> 16; |
271 | 0 | ptrdiff_t row_stride = (dy >> 16) * src_stride; |
272 | 0 | void (*ScaleUVRowDownEven)(const uint8_t* src_uv, ptrdiff_t src_stride, |
273 | 0 | int src_step, uint8_t* dst_uv, int dst_width) = |
274 | 0 | filtering ? ScaleUVRowDownEvenBox_C : ScaleUVRowDownEven_C; |
275 | 0 | (void)src_width; |
276 | 0 | (void)src_height; |
277 | 0 | assert(IS_ALIGNED(src_width, 2)); |
278 | 0 | assert(IS_ALIGNED(src_height, 2)); |
279 | 0 | src_uv += (y >> 16) * src_stride + (x >> 16) * 2; |
280 | | #if defined(HAS_SCALEUVROWDOWNEVEN_SSSE3) |
281 | | if (TestCpuFlag(kCpuHasSSSE3)) { |
282 | | ScaleUVRowDownEven = filtering ? ScaleUVRowDownEvenBox_Any_SSSE3 |
283 | | : ScaleUVRowDownEven_Any_SSSE3; |
284 | | if (IS_ALIGNED(dst_width, 4)) { |
285 | | ScaleUVRowDownEven = |
286 | | filtering ? ScaleUVRowDownEvenBox_SSE2 : ScaleUVRowDownEven_SSSE3; |
287 | | } |
288 | | } |
289 | | #endif |
290 | | #if defined(HAS_SCALEUVROWDOWNEVEN_NEON) |
291 | | if (TestCpuFlag(kCpuHasNEON) && !filtering) { |
292 | | ScaleUVRowDownEven = ScaleUVRowDownEven_Any_NEON; |
293 | | if (IS_ALIGNED(dst_width, 4)) { |
294 | | ScaleUVRowDownEven = ScaleUVRowDownEven_NEON; |
295 | | } |
296 | | } |
297 | | #endif // TODO(fbarchard): Enable Box filter |
298 | | #if defined(HAS_SCALEUVROWDOWNEVENBOX_NEON) |
299 | | if (TestCpuFlag(kCpuHasNEON)) { |
300 | | ScaleUVRowDownEven = filtering ? ScaleUVRowDownEvenBox_Any_NEON |
301 | | : ScaleUVRowDownEven_Any_NEON; |
302 | | if (IS_ALIGNED(dst_width, 4)) { |
303 | | ScaleUVRowDownEven = |
304 | | filtering ? ScaleUVRowDownEvenBox_NEON : ScaleUVRowDownEven_NEON; |
305 | | } |
306 | | } |
307 | | #endif |
308 | | #if defined(HAS_SCALEUVROWDOWNEVEN_RVV) || defined(HAS_SCALEUVROWDOWN4_RVV) |
309 | | if (TestCpuFlag(kCpuHasRVV) && !filtering) { |
310 | | #if defined(HAS_SCALEUVROWDOWNEVEN_RVV) |
311 | | ScaleUVRowDownEven = ScaleUVRowDownEven_RVV; |
312 | | #endif |
313 | | #if defined(HAS_SCALEUVROWDOWN4_RVV) |
314 | | if (col_step == 4) { |
315 | | ScaleUVRowDownEven = ScaleUVRowDown4_RVV; |
316 | | } |
317 | | #endif |
318 | | } |
319 | | #endif |
320 | |
|
321 | 0 | if (filtering == kFilterLinear) { |
322 | 0 | src_stride = 0; |
323 | 0 | } |
324 | 0 | for (j = 0; j < dst_height; ++j) { |
325 | 0 | ScaleUVRowDownEven(src_uv, src_stride, col_step, dst_uv, dst_width); |
326 | 0 | src_uv += row_stride; |
327 | 0 | dst_uv += dst_stride; |
328 | 0 | } |
329 | 0 | } |
330 | | #endif |
331 | | |
332 | | // Scale UV down with bilinear interpolation. |
333 | | #if HAS_SCALEUVBILINEARDOWN |
334 | | static int ScaleUVBilinearDown(int src_width, |
335 | | int src_height, |
336 | | int dst_width, |
337 | | int dst_height, |
338 | | ptrdiff_t src_stride, |
339 | | ptrdiff_t dst_stride, |
340 | | const uint8_t* src_uv, |
341 | | uint8_t* dst_uv, |
342 | | int x, |
343 | | int dx, |
344 | | int y, |
345 | | int dy, |
346 | 0 | enum FilterMode filtering) { |
347 | 0 | int j; |
348 | 0 | void (*InterpolateRow)(uint8_t* dst_uv, const uint8_t* src_uv, |
349 | 0 | ptrdiff_t src_stride, int dst_width, |
350 | 0 | int source_y_fraction) = InterpolateRow_C; |
351 | 0 | void (*ScaleUVFilterCols)(uint8_t* dst_uv, const uint8_t* src_uv, |
352 | 0 | int dst_width, int x, int dx) = |
353 | 0 | (src_width >= 32768) ? ScaleUVFilterCols64_C : ScaleUVFilterCols_C; |
354 | 0 | int64_t xlast = x + (int64_t)(dst_width - 1) * dx; |
355 | 0 | int64_t xl = (dx >= 0) ? x : xlast; |
356 | 0 | int64_t xr = (dx >= 0) ? xlast : x; |
357 | 0 | int clip_src_width; |
358 | 0 | xl = (xl >> 16) & ~3; // Left edge aligned. |
359 | 0 | xr = (xr >> 16) + 1; // Right most pixel used. Bilinear uses 2 pixels. |
360 | 0 | xr = (xr + 1 + 3) & ~3; // 1 beyond 4 pixel aligned right most pixel. |
361 | 0 | if (xr > src_width) { |
362 | 0 | xr = src_width; |
363 | 0 | } |
364 | 0 | clip_src_width = (int)(xr - xl) * 2; // Width aligned to 2. |
365 | 0 | src_uv += xl * 2; |
366 | 0 | x -= (int)(xl << 16); |
367 | 0 | #if defined(HAS_INTERPOLATEROW_AVX2) |
368 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
369 | 0 | InterpolateRow = InterpolateRow_Any_AVX2; |
370 | 0 | if (IS_ALIGNED(clip_src_width, 32)) { |
371 | 0 | InterpolateRow = InterpolateRow_AVX2; |
372 | 0 | } |
373 | 0 | } |
374 | 0 | #endif |
375 | | #if defined(HAS_INTERPOLATEROW_NEON) |
376 | | if (TestCpuFlag(kCpuHasNEON)) { |
377 | | InterpolateRow = InterpolateRow_Any_NEON; |
378 | | if (IS_ALIGNED(clip_src_width, 16)) { |
379 | | InterpolateRow = InterpolateRow_NEON; |
380 | | } |
381 | | } |
382 | | #endif |
383 | | #if defined(HAS_INTERPOLATEROW_SVE2) |
384 | | if (TestCpuFlag(kCpuHasSVE2)) { |
385 | | InterpolateRow = InterpolateRow_SVE2; |
386 | | } |
387 | | #endif |
388 | | #if defined(HAS_INTERPOLATEROW_SME) |
389 | | if (TestCpuFlag(kCpuHasSME)) { |
390 | | InterpolateRow = InterpolateRow_SME; |
391 | | } |
392 | | #endif |
393 | | #if defined(HAS_INTERPOLATEROW_LSX) |
394 | | if (TestCpuFlag(kCpuHasLSX)) { |
395 | | InterpolateRow = InterpolateRow_Any_LSX; |
396 | | if (IS_ALIGNED(clip_src_width, 32)) { |
397 | | InterpolateRow = InterpolateRow_LSX; |
398 | | } |
399 | | } |
400 | | #endif |
401 | | #if defined(HAS_INTERPOLATEROW_RVV) |
402 | | if (TestCpuFlag(kCpuHasRVV)) { |
403 | | InterpolateRow = InterpolateRow_RVV; |
404 | | } |
405 | | #endif |
406 | | #if defined(HAS_SCALEUVFILTERCOLS_SSSE3) |
407 | | if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) { |
408 | | ScaleUVFilterCols = ScaleUVFilterCols_SSSE3; |
409 | | } |
410 | | #endif |
411 | | #if defined(HAS_SCALEUVFILTERCOLS_NEON) |
412 | | if (TestCpuFlag(kCpuHasNEON)) { |
413 | | ScaleUVFilterCols = ScaleUVFilterCols_Any_NEON; |
414 | | if (IS_ALIGNED(dst_width, 4)) { |
415 | | ScaleUVFilterCols = ScaleUVFilterCols_NEON; |
416 | | } |
417 | | } |
418 | | #endif |
419 | | // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear. |
420 | | // Allocate a row of UV. |
421 | 0 | { |
422 | 0 | const int max_y = (src_height - 1) << 16; |
423 | 0 | align_buffer_64(row, clip_src_width * 2); |
424 | 0 | if (!row) |
425 | 0 | return 1; |
426 | 0 | if (y > max_y) { |
427 | 0 | y = max_y; |
428 | 0 | } |
429 | 0 | for (j = 0; j < dst_height; ++j) { |
430 | 0 | int yi = y >> 16; |
431 | 0 | const uint8_t* src = src_uv + yi * src_stride; |
432 | 0 | if (filtering == kFilterLinear) { |
433 | 0 | ScaleUVFilterCols(dst_uv, src, dst_width, x, dx); |
434 | 0 | } else { |
435 | 0 | int yf = (y >> 8) & 255; |
436 | 0 | InterpolateRow(row, src, src_stride, clip_src_width, yf); |
437 | 0 | ScaleUVFilterCols(dst_uv, row, dst_width, x, dx); |
438 | 0 | } |
439 | 0 | dst_uv += dst_stride; |
440 | 0 | y += dy; |
441 | 0 | if (y > max_y) { |
442 | 0 | y = max_y; |
443 | 0 | } |
444 | 0 | } |
445 | 0 | free_aligned_buffer_64(row); |
446 | 0 | } |
447 | 0 | return 0; |
448 | 0 | } |
449 | | #endif |
450 | | |
451 | | // Scale UV up with bilinear interpolation. |
452 | | #if HAS_SCALEUVBILINEARUP |
453 | | static int ScaleUVBilinearUp(int src_width, |
454 | | int src_height, |
455 | | int dst_width, |
456 | | int dst_height, |
457 | | ptrdiff_t src_stride, |
458 | | ptrdiff_t dst_stride, |
459 | | const uint8_t* src_uv, |
460 | | uint8_t* dst_uv, |
461 | | int x, |
462 | | int dx, |
463 | | int y, |
464 | | int dy, |
465 | 0 | enum FilterMode filtering) { |
466 | 0 | assert(src_width > 0); |
467 | 0 | assert(src_height > 0); |
468 | 0 | assert(dst_width > 0); |
469 | 0 | assert(dst_height > 0); |
470 | 0 | assert(dy <= 65536); |
471 | |
|
472 | 0 | int j; |
473 | 0 | void (*InterpolateRow)(uint8_t* dst_uv, const uint8_t* src_uv, |
474 | 0 | ptrdiff_t src_stride, int dst_width, |
475 | 0 | int source_y_fraction) = InterpolateRow_C; |
476 | 0 | void (*ScaleUVFilterCols)(uint8_t* dst_uv, const uint8_t* src_uv, |
477 | 0 | int dst_width, int x, int dx) = |
478 | 0 | filtering ? ScaleUVFilterCols_C : ScaleUVCols_C; |
479 | 0 | const int max_y = (src_height - 1) << 16; |
480 | 0 | #if defined(HAS_INTERPOLATEROW_AVX2) |
481 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
482 | 0 | InterpolateRow = InterpolateRow_Any_AVX2; |
483 | 0 | if (IS_ALIGNED(dst_width, 16)) { |
484 | 0 | InterpolateRow = InterpolateRow_AVX2; |
485 | 0 | } |
486 | 0 | } |
487 | 0 | #endif |
488 | | #if defined(HAS_INTERPOLATEROW_NEON) |
489 | | if (TestCpuFlag(kCpuHasNEON)) { |
490 | | InterpolateRow = InterpolateRow_Any_NEON; |
491 | | if (IS_ALIGNED(dst_width, 8)) { |
492 | | InterpolateRow = InterpolateRow_NEON; |
493 | | } |
494 | | } |
495 | | #endif |
496 | | #if defined(HAS_INTERPOLATEROW_SVE2) |
497 | | if (TestCpuFlag(kCpuHasSVE2)) { |
498 | | InterpolateRow = InterpolateRow_SVE2; |
499 | | } |
500 | | #endif |
501 | | #if defined(HAS_INTERPOLATEROW_SME) |
502 | | if (TestCpuFlag(kCpuHasSME)) { |
503 | | InterpolateRow = InterpolateRow_SME; |
504 | | } |
505 | | #endif |
506 | | #if defined(HAS_INTERPOLATEROW_LSX) |
507 | | if (TestCpuFlag(kCpuHasLSX)) { |
508 | | InterpolateRow = InterpolateRow_Any_LSX; |
509 | | if (IS_ALIGNED(dst_width, 16)) { |
510 | | InterpolateRow = InterpolateRow_LSX; |
511 | | } |
512 | | } |
513 | | #endif |
514 | | #if defined(HAS_INTERPOLATEROW_RVV) |
515 | | if (TestCpuFlag(kCpuHasRVV)) { |
516 | | InterpolateRow = InterpolateRow_RVV; |
517 | | } |
518 | | #endif |
519 | 0 | if (src_width >= 32768) { |
520 | 0 | ScaleUVFilterCols = filtering ? ScaleUVFilterCols64_C : ScaleUVCols64_C; |
521 | 0 | } |
522 | | #if defined(HAS_SCALEUVFILTERCOLS_SSSE3) |
523 | | if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) { |
524 | | ScaleUVFilterCols = ScaleUVFilterCols_SSSE3; |
525 | | } |
526 | | #endif |
527 | | #if defined(HAS_SCALEUVFILTERCOLS_NEON) |
528 | | if (filtering && TestCpuFlag(kCpuHasNEON)) { |
529 | | ScaleUVFilterCols = ScaleUVFilterCols_Any_NEON; |
530 | | if (IS_ALIGNED(dst_width, 8)) { |
531 | | ScaleUVFilterCols = ScaleUVFilterCols_NEON; |
532 | | } |
533 | | } |
534 | | #endif |
535 | | #if defined(HAS_SCALEUVCOLS_SSSE3) |
536 | | if (!filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) { |
537 | | ScaleUVFilterCols = ScaleUVCols_SSSE3; |
538 | | } |
539 | | #endif |
540 | | #if defined(HAS_SCALEUVCOLS_NEON) |
541 | | if (!filtering && TestCpuFlag(kCpuHasNEON)) { |
542 | | ScaleUVFilterCols = ScaleUVCols_Any_NEON; |
543 | | if (IS_ALIGNED(dst_width, 16)) { |
544 | | ScaleUVFilterCols = ScaleUVCols_NEON; |
545 | | } |
546 | | } |
547 | | #endif |
548 | 0 | if (!filtering && src_width * 2 == dst_width && x < 0x8000) { |
549 | 0 | ScaleUVFilterCols = ScaleUVColsUp2_C; |
550 | | #if defined(HAS_SCALEUVCOLSUP2_SSSE3) |
551 | | if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(dst_width, 8)) { |
552 | | ScaleUVFilterCols = ScaleUVColsUp2_SSSE3; |
553 | | } |
554 | | #endif |
555 | 0 | } |
556 | |
|
557 | 0 | if (y > max_y) { |
558 | 0 | y = max_y; |
559 | 0 | } |
560 | |
|
561 | 0 | { |
562 | 0 | int yi = y >> 16; |
563 | 0 | const uint8_t* src = src_uv + yi * src_stride; |
564 | | |
565 | | // Allocate 2 rows of UV. |
566 | 0 | const int row_size = (dst_width * 2 + 15) & ~15; |
567 | 0 | align_buffer_64(row, row_size * 2); |
568 | 0 | if (!row) |
569 | 0 | return 1; |
570 | | |
571 | 0 | uint8_t* rowptr = row; |
572 | 0 | ptrdiff_t rowstride = row_size; |
573 | 0 | int lasty = yi; |
574 | |
|
575 | 0 | ScaleUVFilterCols(rowptr, src, dst_width, x, dx); |
576 | 0 | if (src_height > 1) { |
577 | 0 | src += src_stride; |
578 | 0 | } |
579 | 0 | ScaleUVFilterCols(rowptr + rowstride, src, dst_width, x, dx); |
580 | 0 | if (src_height > 2) { |
581 | 0 | src += src_stride; |
582 | 0 | } |
583 | | |
584 | | // 2-row rolling buffer: |
585 | | // rowptr and (rowptr + rowstride) hold the scaled rows for yi and yi + 1. |
586 | | // Because dy <= 65536 (dy <= 1.0 in 16.16), yi advances in unit steps. |
587 | | // When yi != lasty: |
588 | | // 1. Scale the next source row into the older buffer (rowptr). |
589 | | // 2. Swap buffer pointers (rowptr += rowstride; rowstride = -rowstride;) |
590 | | // so rowptr points to yi and (rowptr + rowstride) points to yi + 1. |
591 | | // 3. Advance src by 1 row if row yi + 2 exists ((y + 65536) < max_y), |
592 | | // otherwise clamp src at (src_height - 1) to avoid reading out of bounds. |
593 | 0 | for (j = 0; j < dst_height; ++j) { |
594 | 0 | if (y > max_y) { |
595 | 0 | y = max_y; |
596 | 0 | } |
597 | 0 | yi = y >> 16; |
598 | 0 | if (yi != lasty) { |
599 | 0 | ScaleUVFilterCols(rowptr, src, dst_width, x, dx); |
600 | 0 | rowptr += rowstride; |
601 | 0 | rowstride = -rowstride; |
602 | 0 | lasty = yi; |
603 | 0 | if ((y + 65536) < max_y) { |
604 | 0 | src += src_stride; |
605 | 0 | } |
606 | 0 | } |
607 | 0 | if (filtering == kFilterLinear) { |
608 | 0 | InterpolateRow(dst_uv, rowptr, 0, dst_width * 2, 0); |
609 | 0 | } else { |
610 | 0 | int yf = (y >> 8) & 255; |
611 | 0 | InterpolateRow(dst_uv, rowptr, rowstride, dst_width * 2, yf); |
612 | 0 | } |
613 | 0 | dst_uv += dst_stride; |
614 | 0 | y += dy; |
615 | 0 | } |
616 | 0 | free_aligned_buffer_64(row); |
617 | 0 | } |
618 | 0 | return 0; |
619 | 0 | } |
620 | | #endif // HAS_SCALEUVBILINEARUP |
621 | | |
622 | | // Scale UV, horizontally up by 2 times. |
623 | | // Uses linear filter horizontally, nearest vertically. |
624 | | // This is an optimized version for scaling up a plane to 2 times of |
625 | | // its original width, using linear interpolation. |
626 | | // This is used to scale U and V planes of NV16 to NV24. |
627 | | static void ScaleUVLinearUp2(int src_width, |
628 | | int src_height, |
629 | | int dst_width, |
630 | | int dst_height, |
631 | | ptrdiff_t src_stride, |
632 | | ptrdiff_t dst_stride, |
633 | | const uint8_t* src_uv, |
634 | 0 | uint8_t* dst_uv) { |
635 | 0 | void (*ScaleRowUp)(const uint8_t* src_uv, uint8_t* dst_uv, int dst_width) = |
636 | 0 | ScaleUVRowUp2_Linear_Any_C; |
637 | 0 | int i; |
638 | 0 | int y; |
639 | 0 | int dy; |
640 | | |
641 | | // This function can only scale up by 2 times horizontally. |
642 | 0 | (void)src_width; |
643 | 0 | assert(src_width == ((dst_width + 1) / 2)); |
644 | |
|
645 | 0 | #ifdef HAS_SCALEUVROWUP2_LINEAR_SSSE3 |
646 | 0 | if (TestCpuFlag(kCpuHasSSSE3)) { |
647 | 0 | ScaleRowUp = ScaleUVRowUp2_Linear_Any_SSSE3; |
648 | 0 | } |
649 | 0 | #endif |
650 | |
|
651 | 0 | #ifdef HAS_SCALEUVROWUP2_LINEAR_AVX2 |
652 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
653 | 0 | ScaleRowUp = ScaleUVRowUp2_Linear_Any_AVX2; |
654 | 0 | } |
655 | 0 | #endif |
656 | |
|
657 | | #ifdef HAS_SCALEUVROWUP2_LINEAR_NEON |
658 | | if (TestCpuFlag(kCpuHasNEON)) { |
659 | | ScaleRowUp = ScaleUVRowUp2_Linear_Any_NEON; |
660 | | } |
661 | | #endif |
662 | |
|
663 | | #ifdef HAS_SCALEUVROWUP2_LINEAR_RVV |
664 | | if (TestCpuFlag(kCpuHasRVV)) { |
665 | | ScaleRowUp = ScaleUVRowUp2_Linear_RVV; |
666 | | } |
667 | | #endif |
668 | |
|
669 | 0 | if (dst_height == 1) { |
670 | 0 | ScaleRowUp(src_uv + ((src_height - 1) / 2) * src_stride, dst_uv, dst_width); |
671 | 0 | } else { |
672 | 0 | dy = FixedDiv(src_height - 1, dst_height - 1); |
673 | 0 | y = (1 << 15) - 1; |
674 | 0 | for (i = 0; i < dst_height; ++i) { |
675 | 0 | ScaleRowUp(src_uv + (y >> 16) * src_stride, dst_uv, dst_width); |
676 | 0 | dst_uv += dst_stride; |
677 | 0 | y += dy; |
678 | 0 | } |
679 | 0 | } |
680 | 0 | } |
681 | | |
682 | | // Scale plane, up by 2 times. |
683 | | // This is an optimized version for scaling up a plane to 2 times of |
684 | | // its original size, using bilinear interpolation. |
685 | | // This is used to scale U and V planes of NV12 to NV24. |
686 | | static void ScaleUVBilinearUp2(int src_width, |
687 | | int src_height, |
688 | | int dst_width, |
689 | | int dst_height, |
690 | | int src_stride, |
691 | | int dst_stride, |
692 | | const uint8_t* src_ptr, |
693 | 0 | uint8_t* dst_ptr) { |
694 | 0 | void (*Scale2RowUp)(const uint8_t* src_ptr, ptrdiff_t src_stride, |
695 | 0 | uint8_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = |
696 | 0 | ScaleUVRowUp2_Bilinear_Any_C; |
697 | 0 | int x; |
698 | | |
699 | | // This function can only scale up by 2 times. |
700 | 0 | (void)src_width; |
701 | 0 | assert(src_width == ((dst_width + 1) / 2)); |
702 | 0 | assert(src_height == ((dst_height + 1) / 2)); |
703 | |
|
704 | 0 | #ifdef HAS_SCALEUVROWUP2_BILINEAR_SSSE3 |
705 | 0 | if (TestCpuFlag(kCpuHasSSSE3)) { |
706 | 0 | Scale2RowUp = ScaleUVRowUp2_Bilinear_Any_SSSE3; |
707 | 0 | } |
708 | 0 | #endif |
709 | |
|
710 | 0 | #ifdef HAS_SCALEUVROWUP2_BILINEAR_AVX2 |
711 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
712 | 0 | Scale2RowUp = ScaleUVRowUp2_Bilinear_Any_AVX2; |
713 | 0 | } |
714 | 0 | #endif |
715 | |
|
716 | | #ifdef HAS_SCALEUVROWUP2_BILINEAR_NEON |
717 | | if (TestCpuFlag(kCpuHasNEON)) { |
718 | | Scale2RowUp = ScaleUVRowUp2_Bilinear_Any_NEON; |
719 | | } |
720 | | #endif |
721 | |
|
722 | | #ifdef HAS_SCALEUVROWUP2_BILINEAR_RVV |
723 | | if (TestCpuFlag(kCpuHasRVV)) { |
724 | | Scale2RowUp = ScaleUVRowUp2_Bilinear_RVV; |
725 | | } |
726 | | #endif |
727 | |
|
728 | 0 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
729 | 0 | dst_ptr += dst_stride; |
730 | 0 | for (x = 0; x < src_height - 1; ++x) { |
731 | 0 | Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); |
732 | 0 | src_ptr += src_stride; |
733 | | // TODO(fbarchard): Test performance of writing one row of destination at a |
734 | | // time. |
735 | 0 | dst_ptr += 2 * dst_stride; |
736 | 0 | } |
737 | 0 | if (!(dst_height & 1)) { |
738 | 0 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | | // Scale 16 bit UV, horizontally up by 2 times. |
743 | | // Uses linear filter horizontally, nearest vertically. |
744 | | // This is an optimized version for scaling up a plane to 2 times of |
745 | | // its original width, using linear interpolation. |
746 | | // This is used to scale U and V planes of P210 to P410. |
747 | | static void ScaleUVLinearUp2_16(int src_width, |
748 | | int src_height, |
749 | | int dst_width, |
750 | | int dst_height, |
751 | | ptrdiff_t src_stride, |
752 | | ptrdiff_t dst_stride, |
753 | | const uint16_t* src_uv, |
754 | 0 | uint16_t* dst_uv) { |
755 | 0 | void (*ScaleRowUp)(const uint16_t* src_uv, uint16_t* dst_uv, int dst_width) = |
756 | 0 | ScaleUVRowUp2_Linear_16_Any_C; |
757 | 0 | int i; |
758 | 0 | int y; |
759 | 0 | int dy; |
760 | | |
761 | | // This function can only scale up by 2 times horizontally. |
762 | 0 | (void)src_width; |
763 | 0 | assert(src_width == ((dst_width + 1) / 2)); |
764 | |
|
765 | 0 | #ifdef HAS_SCALEUVROWUP2_LINEAR_16_SSE41 |
766 | 0 | if (TestCpuFlag(kCpuHasSSE41)) { |
767 | 0 | ScaleRowUp = ScaleUVRowUp2_Linear_16_Any_SSE41; |
768 | 0 | } |
769 | 0 | #endif |
770 | |
|
771 | 0 | #ifdef HAS_SCALEUVROWUP2_LINEAR_16_AVX2 |
772 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
773 | 0 | ScaleRowUp = ScaleUVRowUp2_Linear_16_Any_AVX2; |
774 | 0 | } |
775 | 0 | #endif |
776 | |
|
777 | | #ifdef HAS_SCALEUVROWUP2_LINEAR_16_NEON |
778 | | if (TestCpuFlag(kCpuHasNEON)) { |
779 | | ScaleRowUp = ScaleUVRowUp2_Linear_16_Any_NEON; |
780 | | } |
781 | | #endif |
782 | |
|
783 | 0 | if (dst_height == 1) { |
784 | 0 | ScaleRowUp(src_uv + ((src_height - 1) / 2) * src_stride, dst_uv, dst_width); |
785 | 0 | } else { |
786 | 0 | dy = FixedDiv(src_height - 1, dst_height - 1); |
787 | 0 | y = (1 << 15) - 1; |
788 | 0 | for (i = 0; i < dst_height; ++i) { |
789 | 0 | ScaleRowUp(src_uv + (y >> 16) * src_stride, dst_uv, dst_width); |
790 | 0 | dst_uv += dst_stride; |
791 | 0 | y += dy; |
792 | 0 | } |
793 | 0 | } |
794 | 0 | } |
795 | | |
796 | | // Scale 16 bit UV, up by 2 times. |
797 | | // This is an optimized version for scaling up a plane to 2 times of |
798 | | // its original size, using bilinear interpolation. |
799 | | // This is used to scale U and V planes of P010 to P410. |
800 | | static void ScaleUVBilinearUp2_16(int src_width, |
801 | | int src_height, |
802 | | int dst_width, |
803 | | int dst_height, |
804 | | int src_stride, |
805 | | int dst_stride, |
806 | | const uint16_t* src_ptr, |
807 | 0 | uint16_t* dst_ptr) { |
808 | 0 | void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride, |
809 | 0 | uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = |
810 | 0 | ScaleUVRowUp2_Bilinear_16_Any_C; |
811 | 0 | int x; |
812 | | |
813 | | // This function can only scale up by 2 times. |
814 | 0 | (void)src_width; |
815 | 0 | assert(src_width == ((dst_width + 1) / 2)); |
816 | 0 | assert(src_height == ((dst_height + 1) / 2)); |
817 | |
|
818 | 0 | #ifdef HAS_SCALEUVROWUP2_BILINEAR_16_SSE41 |
819 | 0 | if (TestCpuFlag(kCpuHasSSE41)) { |
820 | 0 | Scale2RowUp = ScaleUVRowUp2_Bilinear_16_Any_SSE41; |
821 | 0 | } |
822 | 0 | #endif |
823 | |
|
824 | 0 | #ifdef HAS_SCALEUVROWUP2_BILINEAR_16_AVX2 |
825 | 0 | if (TestCpuFlag(kCpuHasAVX2)) { |
826 | 0 | Scale2RowUp = ScaleUVRowUp2_Bilinear_16_Any_AVX2; |
827 | 0 | } |
828 | 0 | #endif |
829 | |
|
830 | | #ifdef HAS_SCALEUVROWUP2_BILINEAR_16_NEON |
831 | | if (TestCpuFlag(kCpuHasNEON)) { |
832 | | Scale2RowUp = ScaleUVRowUp2_Bilinear_16_Any_NEON; |
833 | | } |
834 | | #endif |
835 | |
|
836 | 0 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
837 | 0 | dst_ptr += dst_stride; |
838 | 0 | for (x = 0; x < src_height - 1; ++x) { |
839 | 0 | Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); |
840 | 0 | src_ptr += src_stride; |
841 | | // TODO(fbarchard): Test performance of writing one row of destination at a |
842 | | // time. |
843 | 0 | dst_ptr += 2 * dst_stride; |
844 | 0 | } |
845 | 0 | if (!(dst_height & 1)) { |
846 | 0 | Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); |
847 | 0 | } |
848 | 0 | } |
849 | | |
850 | | // Scale UV to/from any dimensions, without interpolation. |
851 | | // Fixed point math is used for performance: The upper 16 bits |
852 | | // of x and dx is the integer part of the source position and |
853 | | // the lower 16 bits are the fixed decimal part. |
854 | | |
855 | | static void ScaleUVSimple(int src_width, |
856 | | int src_height, |
857 | | int dst_width, |
858 | | int dst_height, |
859 | | ptrdiff_t src_stride, |
860 | | ptrdiff_t dst_stride, |
861 | | const uint8_t* src_uv, |
862 | | uint8_t* dst_uv, |
863 | | int x, |
864 | | int dx, |
865 | | int y, |
866 | 0 | int dy) { |
867 | 0 | int j; |
868 | 0 | void (*ScaleUVCols)(uint8_t* dst_uv, const uint8_t* src_uv, int dst_width, |
869 | 0 | int x, int dx) = |
870 | 0 | (src_width >= 32768) ? ScaleUVCols64_C : ScaleUVCols_C; |
871 | 0 | (void)src_height; |
872 | | #if defined(HAS_SCALEUVCOLS_SSSE3) |
873 | | if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) { |
874 | | ScaleUVCols = ScaleUVCols_SSSE3; |
875 | | } |
876 | | #endif |
877 | | #if defined(HAS_SCALEUVCOLS_NEON) |
878 | | if (TestCpuFlag(kCpuHasNEON)) { |
879 | | ScaleUVCols = ScaleUVCols_Any_NEON; |
880 | | if (IS_ALIGNED(dst_width, 8)) { |
881 | | ScaleUVCols = ScaleUVCols_NEON; |
882 | | } |
883 | | } |
884 | | #endif |
885 | 0 | if (src_width * 2 == dst_width && x < 0x8000) { |
886 | 0 | ScaleUVCols = ScaleUVColsUp2_C; |
887 | | #if defined(HAS_SCALEUVCOLSUP2_SSSE3) |
888 | | if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(dst_width, 8)) { |
889 | | ScaleUVCols = ScaleUVColsUp2_SSSE3; |
890 | | } |
891 | | #endif |
892 | 0 | } |
893 | |
|
894 | 0 | for (j = 0; j < dst_height; ++j) { |
895 | 0 | ScaleUVCols(dst_uv, src_uv + (y >> 16) * src_stride, dst_width, x, dx); |
896 | 0 | dst_uv += dst_stride; |
897 | 0 | y += dy; |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | | // Copy UV with optional flipping |
902 | | #if HAS_UVCOPY |
903 | | static int UVCopy(const uint8_t* src_uv, |
904 | | int src_stride_uv, |
905 | | uint8_t* dst_uv, |
906 | | int dst_stride_uv, |
907 | | int width, |
908 | 0 | int height) { |
909 | 0 | if (!src_uv || !dst_uv || width <= 0 || height == 0 || height == INT_MIN) { |
910 | 0 | return -1; |
911 | 0 | } |
912 | | // Negative height means invert the image. |
913 | 0 | if (height < 0) { |
914 | 0 | height = -height; |
915 | 0 | src_uv = src_uv + (height - 1) * (ptrdiff_t)src_stride_uv; |
916 | 0 | src_stride_uv = -src_stride_uv; |
917 | 0 | } |
918 | |
|
919 | 0 | CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, width * 2, height); |
920 | 0 | return 0; |
921 | 0 | } |
922 | | |
923 | | static int UVCopy_16(const uint16_t* src_uv, |
924 | | int src_stride_uv, |
925 | | uint16_t* dst_uv, |
926 | | int dst_stride_uv, |
927 | | int width, |
928 | 0 | int height) { |
929 | 0 | if (!src_uv || !dst_uv || width <= 0 || height == 0 || height == INT_MIN) { |
930 | 0 | return -1; |
931 | 0 | } |
932 | | // Negative height means invert the image. |
933 | 0 | if (height < 0) { |
934 | 0 | height = -height; |
935 | 0 | src_uv = src_uv + (height - 1) * (ptrdiff_t)src_stride_uv; |
936 | 0 | src_stride_uv = -src_stride_uv; |
937 | 0 | } |
938 | |
|
939 | 0 | CopyPlane_16(src_uv, src_stride_uv, dst_uv, dst_stride_uv, width * 2, height); |
940 | 0 | return 0; |
941 | 0 | } |
942 | | #endif // HAS_UVCOPY |
943 | | |
944 | | // Scale a UV plane (from NV12) |
945 | | // This function in turn calls a scaling function |
946 | | // suitable for handling the desired resolutions. |
947 | | static int ScaleUV(const uint8_t* src, |
948 | | int src_stride, |
949 | | int src_width, |
950 | | int src_height, |
951 | | uint8_t* dst, |
952 | | int dst_stride, |
953 | | int dst_width, |
954 | | int dst_height, |
955 | | int clip_x, |
956 | | int clip_y, |
957 | | int clip_width, |
958 | | int clip_height, |
959 | 0 | enum FilterMode filtering) { |
960 | | // Initial source x/y coordinate and step values as 16.16 fixed point. |
961 | 0 | int x = 0; |
962 | 0 | int y = 0; |
963 | 0 | int dx = 0; |
964 | 0 | int dy = 0; |
965 | | // UV does not support box filter yet, but allow the user to pass it. |
966 | | // Simplify filtering when possible. |
967 | 0 | filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, |
968 | 0 | filtering); |
969 | | |
970 | | // Negative src_height means invert the image. |
971 | 0 | if (src_height < 0) { |
972 | 0 | src_height = -src_height; |
973 | 0 | src = src + (src_height - 1) * (ptrdiff_t)src_stride; |
974 | 0 | src_stride = -src_stride; |
975 | 0 | } |
976 | 0 | ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, |
977 | 0 | &dx, &dy); |
978 | 0 | src_width = Abs(src_width); |
979 | 0 | if (clip_x) { |
980 | 0 | int64_t clipf = (int64_t)(clip_x)*dx; |
981 | 0 | x += (clipf & 0xffff); |
982 | 0 | src += (clipf >> 16) * 2; |
983 | 0 | dst += clip_x * 2; |
984 | 0 | } |
985 | 0 | if (clip_y) { |
986 | 0 | int64_t clipf = (int64_t)(clip_y)*dy; |
987 | 0 | y += (clipf & 0xffff); |
988 | 0 | src += (clipf >> 16) * (ptrdiff_t)src_stride; |
989 | 0 | dst += clip_y * (ptrdiff_t)dst_stride; |
990 | 0 | } |
991 | | |
992 | | // Special case for integer step values. |
993 | 0 | if (((dx | dy) & 0xffff) == 0) { |
994 | 0 | if (!dx || !dy) { // 1 pixel wide and/or tall. |
995 | 0 | filtering = kFilterNone; |
996 | 0 | } else { |
997 | | // Optimized even scale down. ie 2, 4, 6, 8, 10x. |
998 | 0 | if (!(dx & 0x10000) && !(dy & 0x10000)) { |
999 | 0 | #if HAS_SCALEUVDOWN2 |
1000 | 0 | if (dx == 0x20000 && dy == 0x20000) { |
1001 | | // Optimized 1/2 downsample. |
1002 | 0 | ScaleUVDown2(src_width, src_height, clip_width, clip_height, |
1003 | 0 | src_stride, dst_stride, src, dst, x, dx, y, dy, |
1004 | 0 | filtering); |
1005 | 0 | return 0; |
1006 | 0 | } |
1007 | 0 | #endif |
1008 | 0 | #if HAS_SCALEUVDOWN4BOX |
1009 | 0 | if (dx == 0x40000 && dy == 0x40000 && filtering == kFilterBox) { |
1010 | | // Optimized 1/4 box downsample. |
1011 | 0 | return ScaleUVDown4Box(src_width, src_height, clip_width, clip_height, |
1012 | 0 | src_stride, dst_stride, src, dst, x, dx, y, |
1013 | 0 | dy); |
1014 | 0 | } |
1015 | 0 | #endif |
1016 | 0 | #if HAS_SCALEUVDOWNEVEN |
1017 | 0 | ScaleUVDownEven(src_width, src_height, clip_width, clip_height, |
1018 | 0 | src_stride, dst_stride, src, dst, x, dx, y, dy, |
1019 | 0 | filtering); |
1020 | 0 | return 0; |
1021 | 0 | #endif |
1022 | 0 | } |
1023 | | // Optimized odd scale down. ie 3, 5, 7, 9x. |
1024 | 0 | if ((dx & 0x10000) && (dy & 0x10000)) { |
1025 | 0 | filtering = kFilterNone; |
1026 | 0 | #ifdef HAS_UVCOPY |
1027 | 0 | if (dx == 0x10000 && dy == 0x10000) { |
1028 | | // Straight copy. |
1029 | 0 | return UVCopy(src + (y >> 16) * (ptrdiff_t)src_stride + (x >> 16) * 2, |
1030 | 0 | src_stride, dst, dst_stride, clip_width, clip_height); |
1031 | 0 | } |
1032 | 0 | #endif |
1033 | 0 | } |
1034 | 0 | } |
1035 | 0 | } |
1036 | | // HAS_SCALEPLANEVERTICAL |
1037 | 0 | if (dx == 0x10000 && (x & 0xffff) == 0) { |
1038 | | // Arbitrary scale vertically, but unscaled horizontally. |
1039 | 0 | ScalePlaneVertical(src_height, clip_width, clip_height, src_stride, |
1040 | 0 | dst_stride, src, dst, x, y, dy, /*bpp=*/2, filtering); |
1041 | 0 | return 0; |
1042 | 0 | } |
1043 | 0 | if ((filtering == kFilterLinear) && ((dst_width + 1) / 2 == src_width)) { |
1044 | 0 | ScaleUVLinearUp2(src_width, src_height, clip_width, clip_height, src_stride, |
1045 | 0 | dst_stride, src, dst); |
1046 | 0 | return 0; |
1047 | 0 | } |
1048 | 0 | if ((clip_height + 1) / 2 == src_height && |
1049 | 0 | (clip_width + 1) / 2 == src_width && |
1050 | 0 | (filtering == kFilterBilinear || filtering == kFilterBox)) { |
1051 | 0 | ScaleUVBilinearUp2(src_width, src_height, clip_width, clip_height, |
1052 | 0 | src_stride, dst_stride, src, dst); |
1053 | 0 | return 0; |
1054 | 0 | } |
1055 | 0 | #if HAS_SCALEUVBILINEARUP |
1056 | 0 | if (filtering && dy < 65536) { |
1057 | 0 | return ScaleUVBilinearUp(src_width, src_height, clip_width, clip_height, |
1058 | 0 | src_stride, dst_stride, src, dst, x, dx, y, dy, |
1059 | 0 | filtering); |
1060 | 0 | } |
1061 | 0 | #endif |
1062 | 0 | #if HAS_SCALEUVBILINEARDOWN |
1063 | 0 | if (filtering) { |
1064 | 0 | return ScaleUVBilinearDown(src_width, src_height, clip_width, clip_height, |
1065 | 0 | src_stride, dst_stride, src, dst, x, dx, y, dy, |
1066 | 0 | filtering); |
1067 | 0 | } |
1068 | 0 | #endif |
1069 | 0 | ScaleUVSimple(src_width, src_height, clip_width, clip_height, src_stride, |
1070 | 0 | dst_stride, src, dst, x, dx, y, dy); |
1071 | 0 | return 0; |
1072 | 0 | } |
1073 | | |
1074 | | // Scale an UV image. |
1075 | | LIBYUV_API |
1076 | | int UVScale(const uint8_t* src_uv, |
1077 | | int src_stride_uv, |
1078 | | int src_width, |
1079 | | int src_height, |
1080 | | uint8_t* dst_uv, |
1081 | | int dst_stride_uv, |
1082 | | int dst_width, |
1083 | | int dst_height, |
1084 | 0 | enum FilterMode filtering) { |
1085 | 0 | if (!src_uv || src_width <= 0 || src_height == 0 || src_width > 32768 || |
1086 | 0 | src_height < -32768 || src_height > 32768 || !dst_uv || dst_width <= 0 || |
1087 | 0 | dst_height <= 0) { |
1088 | 0 | return -1; |
1089 | 0 | } |
1090 | 0 | return ScaleUV(src_uv, src_stride_uv, src_width, src_height, dst_uv, |
1091 | 0 | dst_stride_uv, dst_width, dst_height, 0, 0, dst_width, |
1092 | 0 | dst_height, filtering); |
1093 | 0 | } |
1094 | | |
1095 | | // Scale a 16 bit UV image. |
1096 | | // This function is currently incomplete, it can't handle all cases. |
1097 | | LIBYUV_API |
1098 | | int UVScale_16(const uint16_t* src_uv, |
1099 | | int src_stride_uv, |
1100 | | int src_width, |
1101 | | int src_height, |
1102 | | uint16_t* dst_uv, |
1103 | | int dst_stride_uv, |
1104 | | int dst_width, |
1105 | | int dst_height, |
1106 | 0 | enum FilterMode filtering) { |
1107 | 0 | int dy = 0; |
1108 | |
|
1109 | 0 | if (!src_uv || src_width <= 0 || src_height == 0 || src_height == INT_MIN || |
1110 | 0 | src_width > 32768 || src_height > 32768 || !dst_uv || dst_width <= 0 || |
1111 | 0 | dst_height <= 0) { |
1112 | 0 | return -1; |
1113 | 0 | } |
1114 | | |
1115 | | // UV does not support box filter yet, but allow the user to pass it. |
1116 | | // Simplify filtering when possible. |
1117 | 0 | filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, |
1118 | 0 | filtering); |
1119 | | |
1120 | | // Negative src_height means invert the image. |
1121 | 0 | if (src_height < 0) { |
1122 | 0 | src_height = -src_height; |
1123 | 0 | src_uv = src_uv + (src_height - 1) * (ptrdiff_t)src_stride_uv; |
1124 | 0 | src_stride_uv = -src_stride_uv; |
1125 | 0 | } |
1126 | 0 | src_width = Abs(src_width); |
1127 | |
|
1128 | 0 | #ifdef HAS_UVCOPY |
1129 | 0 | if (!filtering && src_width == dst_width && (src_height % dst_height == 0)) { |
1130 | 0 | if (dst_height == 1) { |
1131 | 0 | return UVCopy_16( |
1132 | 0 | src_uv + ((src_height - 1) / 2) * (ptrdiff_t)src_stride_uv, |
1133 | 0 | src_stride_uv, dst_uv, dst_stride_uv, dst_width, dst_height); |
1134 | 0 | } |
1135 | 0 | dy = src_height / dst_height; |
1136 | 0 | if (src_stride_uv > INT_MAX / dy) { |
1137 | 0 | return -1; |
1138 | 0 | } |
1139 | 0 | return UVCopy_16(src_uv + ((dy - 1) / 2) * (ptrdiff_t)src_stride_uv, |
1140 | 0 | dy * src_stride_uv, dst_uv, dst_stride_uv, dst_width, |
1141 | 0 | dst_height); |
1142 | 0 | } |
1143 | 0 | #endif |
1144 | | |
1145 | 0 | if ((filtering == kFilterLinear) && ((dst_width + 1) / 2 == src_width)) { |
1146 | 0 | ScaleUVLinearUp2_16(src_width, src_height, dst_width, dst_height, |
1147 | 0 | src_stride_uv, dst_stride_uv, src_uv, dst_uv); |
1148 | 0 | return 0; |
1149 | 0 | } |
1150 | | |
1151 | 0 | if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && |
1152 | 0 | (filtering == kFilterBilinear || filtering == kFilterBox)) { |
1153 | 0 | ScaleUVBilinearUp2_16(src_width, src_height, dst_width, dst_height, |
1154 | 0 | src_stride_uv, dst_stride_uv, src_uv, dst_uv); |
1155 | 0 | return 0; |
1156 | 0 | } |
1157 | | |
1158 | 0 | return -1; |
1159 | 0 | } |
1160 | | |
1161 | | #ifdef __cplusplus |
1162 | | } // extern "C" |
1163 | | } // namespace libyuv |
1164 | | #endif |