• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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