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