• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2011 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 CopyARGB
18 #include "libyuv/row.h"
19 #include "libyuv/scale_argb.h"
20 #include "libyuv/scale_row.h"
21 
22 #ifdef __cplusplus
23 namespace libyuv {
24 extern "C" {
25 #endif
26 
Abs(int v)27 static __inline int Abs(int v) {
28   return v >= 0 ? v : -v;
29 }
30 
31 // ScaleARGB ARGB, 1/2
32 // This is an optimized version for scaling down a ARGB to 1/2 of
33 // its original size.
ScaleARGBDown2(int src_width,int src_height,int dst_width,int dst_height,int src_stride,int dst_stride,const uint8_t * src_argb,uint8_t * dst_argb,int x,int dx,int y,int dy,enum FilterMode filtering)34 static void ScaleARGBDown2(int src_width,
35                            int src_height,
36                            int dst_width,
37                            int dst_height,
38                            int src_stride,
39                            int dst_stride,
40                            const uint8_t* src_argb,
41                            uint8_t* dst_argb,
42                            int x,
43                            int dx,
44                            int y,
45                            int dy,
46                            enum FilterMode filtering) {
47   int j;
48   int row_stride = src_stride * (dy >> 16);
49   void (*ScaleARGBRowDown2)(const uint8_t* src_argb, ptrdiff_t src_stride,
50                             uint8_t* dst_argb, int dst_width) =
51       filtering == kFilterNone
52           ? ScaleARGBRowDown2_C
53           : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_C
54                                         : ScaleARGBRowDown2Box_C);
55   (void)src_width;
56   (void)src_height;
57   (void)dx;
58   assert(dx == 65536 * 2);      // Test scale factor of 2.
59   assert((dy & 0x1ffff) == 0);  // Test vertical scale is multiple of 2.
60   // Advance to odd row, even column.
61   if (filtering == kFilterBilinear) {
62     src_argb += (y >> 16) * (intptr_t)src_stride + (x >> 16) * 4;
63   } else {
64     src_argb += (y >> 16) * (intptr_t)src_stride + ((x >> 16) - 1) * 4;
65   }
66 
67 #if defined(HAS_SCALEARGBROWDOWN2_SSE2)
68   if (TestCpuFlag(kCpuHasSSE2)) {
69     ScaleARGBRowDown2 =
70         filtering == kFilterNone
71             ? ScaleARGBRowDown2_Any_SSE2
72             : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_SSE2
73                                           : ScaleARGBRowDown2Box_Any_SSE2);
74     if (IS_ALIGNED(dst_width, 4)) {
75       ScaleARGBRowDown2 =
76           filtering == kFilterNone
77               ? ScaleARGBRowDown2_SSE2
78               : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_SSE2
79                                             : ScaleARGBRowDown2Box_SSE2);
80     }
81   }
82 #endif
83 #if defined(HAS_SCALEARGBROWDOWN2_NEON)
84   if (TestCpuFlag(kCpuHasNEON)) {
85     ScaleARGBRowDown2 =
86         filtering == kFilterNone
87             ? ScaleARGBRowDown2_Any_NEON
88             : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_NEON
89                                           : ScaleARGBRowDown2Box_Any_NEON);
90     if (IS_ALIGNED(dst_width, 8)) {
91       ScaleARGBRowDown2 =
92           filtering == kFilterNone
93               ? ScaleARGBRowDown2_NEON
94               : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_NEON
95                                             : ScaleARGBRowDown2Box_NEON);
96     }
97   }
98 #endif
99 #if defined(HAS_SCALEARGBROWDOWN2_MSA)
100   if (TestCpuFlag(kCpuHasMSA)) {
101     ScaleARGBRowDown2 =
102         filtering == kFilterNone
103             ? ScaleARGBRowDown2_Any_MSA
104             : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_MSA
105                                           : ScaleARGBRowDown2Box_Any_MSA);
106     if (IS_ALIGNED(dst_width, 4)) {
107       ScaleARGBRowDown2 =
108           filtering == kFilterNone
109               ? ScaleARGBRowDown2_MSA
110               : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_MSA
111                                             : ScaleARGBRowDown2Box_MSA);
112     }
113   }
114 #endif
115 #if defined(HAS_SCALEARGBROWDOWN2_LSX)
116   if (TestCpuFlag(kCpuHasLSX)) {
117     ScaleARGBRowDown2 =
118         filtering == kFilterNone
119             ? ScaleARGBRowDown2_Any_LSX
120             : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_Any_LSX
121                                           : ScaleARGBRowDown2Box_Any_LSX);
122     if (IS_ALIGNED(dst_width, 4)) {
123       ScaleARGBRowDown2 =
124           filtering == kFilterNone
125               ? ScaleARGBRowDown2_LSX
126               : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_LSX
127                                             : ScaleARGBRowDown2Box_LSX);
128     }
129   }
130 #endif
131 #if defined(HAS_SCALEARGBROWDOWN2_RVV)
132   if (TestCpuFlag(kCpuHasRVV)) {
133     ScaleARGBRowDown2 =
134         filtering == kFilterNone
135             ? ScaleARGBRowDown2_RVV
136             : (filtering == kFilterLinear ? ScaleARGBRowDown2Linear_RVV
137                                           : ScaleARGBRowDown2Box_RVV);
138   }
139 #endif
140 
141   if (filtering == kFilterLinear) {
142     src_stride = 0;
143   }
144   for (j = 0; j < dst_height; ++j) {
145     ScaleARGBRowDown2(src_argb, src_stride, dst_argb, dst_width);
146     src_argb += row_stride;
147     dst_argb += dst_stride;
148   }
149 }
150 
151 // ScaleARGB ARGB, 1/4
152 // This is an optimized version for scaling down a ARGB to 1/4 of
153 // its original size.
ScaleARGBDown4Box(int src_width,int src_height,int dst_width,int dst_height,int src_stride,int dst_stride,const uint8_t * src_argb,uint8_t * dst_argb,int x,int dx,int y,int dy)154 static int ScaleARGBDown4Box(int src_width,
155                              int src_height,
156                              int dst_width,
157                              int dst_height,
158                              int src_stride,
159                              int dst_stride,
160                              const uint8_t* src_argb,
161                              uint8_t* dst_argb,
162                              int x,
163                              int dx,
164                              int y,
165                              int dy) {
166   int j;
167   // Allocate 2 rows of ARGB.
168   const int row_size = (dst_width * 2 * 4 + 31) & ~31;
169   // TODO(fbarchard): Remove this row buffer and implement a ScaleARGBRowDown4
170   // but implemented via a 2 pass wrapper that uses a very small array on the
171   // stack with a horizontal loop.
172   align_buffer_64(row, row_size * 2);
173   if (!row)
174     return 1;
175   int row_stride = src_stride * (dy >> 16);
176   void (*ScaleARGBRowDown2)(const uint8_t* src_argb, ptrdiff_t src_stride,
177                             uint8_t* dst_argb, int dst_width) =
178       ScaleARGBRowDown2Box_C;
179   // Advance to odd row, even column.
180   src_argb += (y >> 16) * (intptr_t)src_stride + (x >> 16) * 4;
181   (void)src_width;
182   (void)src_height;
183   (void)dx;
184   assert(dx == 65536 * 4);      // Test scale factor of 4.
185   assert((dy & 0x3ffff) == 0);  // Test vertical scale is multiple of 4.
186 #if defined(HAS_SCALEARGBROWDOWN2_SSE2)
187   if (TestCpuFlag(kCpuHasSSE2)) {
188     ScaleARGBRowDown2 = ScaleARGBRowDown2Box_Any_SSE2;
189     if (IS_ALIGNED(dst_width, 4)) {
190       ScaleARGBRowDown2 = ScaleARGBRowDown2Box_SSE2;
191     }
192   }
193 #endif
194 #if defined(HAS_SCALEARGBROWDOWN2_NEON)
195   if (TestCpuFlag(kCpuHasNEON)) {
196     ScaleARGBRowDown2 = ScaleARGBRowDown2Box_Any_NEON;
197     if (IS_ALIGNED(dst_width, 8)) {
198       ScaleARGBRowDown2 = ScaleARGBRowDown2Box_NEON;
199     }
200   }
201 #endif
202 #if defined(HAS_SCALEARGBROWDOWN2_RVV)
203   if (TestCpuFlag(kCpuHasRVV)) {
204     ScaleARGBRowDown2 = ScaleARGBRowDown2Box_RVV;
205   }
206 #endif
207 
208   for (j = 0; j < dst_height; ++j) {
209     ScaleARGBRowDown2(src_argb, src_stride, row, dst_width * 2);
210     ScaleARGBRowDown2(src_argb + src_stride * 2, src_stride, row + row_size,
211                       dst_width * 2);
212     ScaleARGBRowDown2(row, row_size, dst_argb, dst_width);
213     src_argb += row_stride;
214     dst_argb += dst_stride;
215   }
216   free_aligned_buffer_64(row);
217   return 0;
218 }
219 
220 // ScaleARGB ARGB Even
221 // This is an optimized version for scaling down a ARGB to even
222 // multiple of its original size.
ScaleARGBDownEven(int src_width,int src_height,int dst_width,int dst_height,int src_stride,int dst_stride,const uint8_t * src_argb,uint8_t * dst_argb,int x,int dx,int y,int dy,enum FilterMode filtering)223 static void ScaleARGBDownEven(int src_width,
224                               int src_height,
225                               int dst_width,
226                               int dst_height,
227                               int src_stride,
228                               int dst_stride,
229                               const uint8_t* src_argb,
230                               uint8_t* dst_argb,
231                               int x,
232                               int dx,
233                               int y,
234                               int dy,
235                               enum FilterMode filtering) {
236   int j;
237   int col_step = dx >> 16;
238   ptrdiff_t row_stride = (ptrdiff_t)((dy >> 16) * (intptr_t)src_stride);
239   void (*ScaleARGBRowDownEven)(const uint8_t* src_argb, ptrdiff_t src_stride,
240                                int src_step, uint8_t* dst_argb, int dst_width) =
241       filtering ? ScaleARGBRowDownEvenBox_C : ScaleARGBRowDownEven_C;
242   (void)src_width;
243   (void)src_height;
244   assert(IS_ALIGNED(src_width, 2));
245   assert(IS_ALIGNED(src_height, 2));
246   src_argb += (y >> 16) * (intptr_t)src_stride + (x >> 16) * 4;
247 #if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
248   if (TestCpuFlag(kCpuHasSSE2)) {
249     ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_SSE2
250                                      : ScaleARGBRowDownEven_Any_SSE2;
251     if (IS_ALIGNED(dst_width, 4)) {
252       ScaleARGBRowDownEven =
253           filtering ? ScaleARGBRowDownEvenBox_SSE2 : ScaleARGBRowDownEven_SSE2;
254     }
255   }
256 #endif
257 #if defined(HAS_SCALEARGBROWDOWNEVEN_NEON)
258   if (TestCpuFlag(kCpuHasNEON)) {
259     ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_NEON
260                                      : ScaleARGBRowDownEven_Any_NEON;
261     if (IS_ALIGNED(dst_width, 4)) {
262       ScaleARGBRowDownEven =
263           filtering ? ScaleARGBRowDownEvenBox_NEON : ScaleARGBRowDownEven_NEON;
264     }
265   }
266 #endif
267 #if defined(HAS_SCALEARGBROWDOWNEVEN_MSA)
268   if (TestCpuFlag(kCpuHasMSA)) {
269     ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_MSA
270                                      : ScaleARGBRowDownEven_Any_MSA;
271     if (IS_ALIGNED(dst_width, 4)) {
272       ScaleARGBRowDownEven =
273           filtering ? ScaleARGBRowDownEvenBox_MSA : ScaleARGBRowDownEven_MSA;
274     }
275   }
276 #endif
277 #if defined(HAS_SCALEARGBROWDOWNEVEN_LSX)
278   if (TestCpuFlag(kCpuHasLSX)) {
279     ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenBox_Any_LSX
280                                      : ScaleARGBRowDownEven_Any_LSX;
281     if (IS_ALIGNED(dst_width, 4)) {
282       ScaleARGBRowDownEven =
283           filtering ? ScaleARGBRowDownEvenBox_LSX : ScaleARGBRowDownEven_LSX;
284     }
285   }
286 #endif
287 #if defined(HAS_SCALEARGBROWDOWNEVENBOX_RVV)
288   if (filtering && TestCpuFlag(kCpuHasRVV)) {
289     ScaleARGBRowDownEven = ScaleARGBRowDownEvenBox_RVV;
290   }
291 #endif
292 #if defined(HAS_SCALEARGBROWDOWNEVEN_RVV)
293   if (!filtering && TestCpuFlag(kCpuHasRVV)) {
294     ScaleARGBRowDownEven = ScaleARGBRowDownEven_RVV;
295   }
296 #endif
297 
298   if (filtering == kFilterLinear) {
299     src_stride = 0;
300   }
301   for (j = 0; j < dst_height; ++j) {
302     ScaleARGBRowDownEven(src_argb, src_stride, col_step, dst_argb, dst_width);
303     src_argb += row_stride;
304     dst_argb += dst_stride;
305   }
306 }
307 
308 // Scale ARGB down with bilinear interpolation.
ScaleARGBBilinearDown(int src_width,int src_height,int dst_width,int dst_height,int src_stride,int dst_stride,const uint8_t * src_argb,uint8_t * dst_argb,int x,int dx,int y,int dy,enum FilterMode filtering)309 static int ScaleARGBBilinearDown(int src_width,
310                                  int src_height,
311                                  int dst_width,
312                                  int dst_height,
313                                  int src_stride,
314                                  int dst_stride,
315                                  const uint8_t* src_argb,
316                                  uint8_t* dst_argb,
317                                  int x,
318                                  int dx,
319                                  int y,
320                                  int dy,
321                                  enum FilterMode filtering) {
322   int j;
323   void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
324                          ptrdiff_t src_stride, int dst_width,
325                          int source_y_fraction) = InterpolateRow_C;
326   void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb,
327                               int dst_width, int x, int dx) =
328       (src_width >= 32768) ? ScaleARGBFilterCols64_C : ScaleARGBFilterCols_C;
329   int64_t xlast = x + (int64_t)(dst_width - 1) * dx;
330   int64_t xl = (dx >= 0) ? x : xlast;
331   int64_t xr = (dx >= 0) ? xlast : x;
332   int clip_src_width;
333   xl = (xl >> 16) & ~3;    // Left edge aligned.
334   xr = (xr >> 16) + 1;     // Right most pixel used.  Bilinear uses 2 pixels.
335   xr = (xr + 1 + 3) & ~3;  // 1 beyond 4 pixel aligned right most pixel.
336   if (xr > src_width) {
337     xr = src_width;
338   }
339   clip_src_width = (int)(xr - xl) * 4;  // Width aligned to 4.
340   src_argb += xl * 4;
341   x -= (int)(xl << 16);
342 #if defined(HAS_INTERPOLATEROW_SSSE3)
343   if (TestCpuFlag(kCpuHasSSSE3)) {
344     InterpolateRow = InterpolateRow_Any_SSSE3;
345     if (IS_ALIGNED(clip_src_width, 16)) {
346       InterpolateRow = InterpolateRow_SSSE3;
347     }
348   }
349 #endif
350 #if defined(HAS_INTERPOLATEROW_AVX2)
351   if (TestCpuFlag(kCpuHasAVX2)) {
352     InterpolateRow = InterpolateRow_Any_AVX2;
353     if (IS_ALIGNED(clip_src_width, 32)) {
354       InterpolateRow = InterpolateRow_AVX2;
355     }
356   }
357 #endif
358 #if defined(HAS_INTERPOLATEROW_NEON)
359   if (TestCpuFlag(kCpuHasNEON)) {
360     InterpolateRow = InterpolateRow_Any_NEON;
361     if (IS_ALIGNED(clip_src_width, 16)) {
362       InterpolateRow = InterpolateRow_NEON;
363     }
364   }
365 #endif
366 #if defined(HAS_INTERPOLATEROW_MSA)
367   if (TestCpuFlag(kCpuHasMSA)) {
368     InterpolateRow = InterpolateRow_Any_MSA;
369     if (IS_ALIGNED(clip_src_width, 32)) {
370       InterpolateRow = InterpolateRow_MSA;
371     }
372   }
373 #endif
374 #if defined(HAS_INTERPOLATEROW_LSX)
375   if (TestCpuFlag(kCpuHasLSX)) {
376     InterpolateRow = InterpolateRow_Any_LSX;
377     if (IS_ALIGNED(clip_src_width, 32)) {
378       InterpolateRow = InterpolateRow_LSX;
379     }
380   }
381 #endif
382 #if defined(HAS_INTERPOLATEROW_RVV)
383   if (TestCpuFlag(kCpuHasRVV)) {
384     InterpolateRow = InterpolateRow_RVV;
385   }
386 #endif
387 #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
388   if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
389     ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
390   }
391 #endif
392 #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
393   if (TestCpuFlag(kCpuHasNEON)) {
394     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
395     if (IS_ALIGNED(dst_width, 4)) {
396       ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
397     }
398   }
399 #endif
400 #if defined(HAS_SCALEARGBFILTERCOLS_MSA)
401   if (TestCpuFlag(kCpuHasMSA)) {
402     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_MSA;
403     if (IS_ALIGNED(dst_width, 8)) {
404       ScaleARGBFilterCols = ScaleARGBFilterCols_MSA;
405     }
406   }
407 #endif
408 #if defined(HAS_SCALEARGBFILTERCOLS_LSX)
409   if (TestCpuFlag(kCpuHasLSX)) {
410     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_LSX;
411     if (IS_ALIGNED(dst_width, 8)) {
412       ScaleARGBFilterCols = ScaleARGBFilterCols_LSX;
413     }
414   }
415 #endif
416   // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
417   // Allocate a row of ARGB.
418   {
419     align_buffer_64(row, clip_src_width * 4);
420     if (!row)
421       return 1;
422 
423     const int max_y = (src_height - 1) << 16;
424     if (y > max_y) {
425       y = max_y;
426     }
427     for (j = 0; j < dst_height; ++j) {
428       int yi = y >> 16;
429       const uint8_t* src = src_argb + yi * (intptr_t)src_stride;
430       if (filtering == kFilterLinear) {
431         ScaleARGBFilterCols(dst_argb, src, dst_width, x, dx);
432       } else {
433         int yf = (y >> 8) & 255;
434         InterpolateRow(row, src, src_stride, clip_src_width, yf);
435         ScaleARGBFilterCols(dst_argb, row, dst_width, x, dx);
436       }
437       dst_argb += dst_stride;
438       y += dy;
439       if (y > max_y) {
440         y = max_y;
441       }
442     }
443     free_aligned_buffer_64(row);
444   }
445   return 0;
446 }
447 
448 // Scale ARGB up with bilinear interpolation.
ScaleARGBBilinearUp(int src_width,int src_height,int dst_width,int dst_height,int src_stride,int dst_stride,const uint8_t * src_argb,uint8_t * dst_argb,int x,int dx,int y,int dy,enum FilterMode filtering)449 static int ScaleARGBBilinearUp(int src_width,
450                                int src_height,
451                                int dst_width,
452                                int dst_height,
453                                int src_stride,
454                                int dst_stride,
455                                const uint8_t* src_argb,
456                                uint8_t* dst_argb,
457                                int x,
458                                int dx,
459                                int y,
460                                int dy,
461                                enum FilterMode filtering) {
462   int j;
463   void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
464                          ptrdiff_t src_stride, int dst_width,
465                          int source_y_fraction) = InterpolateRow_C;
466   void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb,
467                               int dst_width, int x, int dx) =
468       filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
469   const int max_y = (src_height - 1) << 16;
470 #if defined(HAS_INTERPOLATEROW_SSSE3)
471   if (TestCpuFlag(kCpuHasSSSE3)) {
472     InterpolateRow = InterpolateRow_Any_SSSE3;
473     if (IS_ALIGNED(dst_width, 4)) {
474       InterpolateRow = InterpolateRow_SSSE3;
475     }
476   }
477 #endif
478 #if defined(HAS_INTERPOLATEROW_AVX2)
479   if (TestCpuFlag(kCpuHasAVX2)) {
480     InterpolateRow = InterpolateRow_Any_AVX2;
481     if (IS_ALIGNED(dst_width, 8)) {
482       InterpolateRow = InterpolateRow_AVX2;
483     }
484   }
485 #endif
486 #if defined(HAS_INTERPOLATEROW_NEON)
487   if (TestCpuFlag(kCpuHasNEON)) {
488     InterpolateRow = InterpolateRow_Any_NEON;
489     if (IS_ALIGNED(dst_width, 4)) {
490       InterpolateRow = InterpolateRow_NEON;
491     }
492   }
493 #endif
494 #if defined(HAS_INTERPOLATEROW_MSA)
495   if (TestCpuFlag(kCpuHasMSA)) {
496     InterpolateRow = InterpolateRow_Any_MSA;
497     if (IS_ALIGNED(dst_width, 8)) {
498       InterpolateRow = InterpolateRow_MSA;
499     }
500   }
501 #endif
502 #if defined(HAS_INTERPOLATEROW_LSX)
503   if (TestCpuFlag(kCpuHasLSX)) {
504     InterpolateRow = InterpolateRow_Any_LSX;
505     if (IS_ALIGNED(dst_width, 8)) {
506       InterpolateRow = InterpolateRow_LSX;
507     }
508   }
509 #endif
510 #if defined(HAS_INTERPOLATEROW_RVV)
511   if (TestCpuFlag(kCpuHasRVV)) {
512     InterpolateRow = InterpolateRow_RVV;
513   }
514 #endif
515   if (src_width >= 32768) {
516     ScaleARGBFilterCols =
517         filtering ? ScaleARGBFilterCols64_C : ScaleARGBCols64_C;
518   }
519 #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
520   if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
521     ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
522   }
523 #endif
524 #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
525   if (filtering && TestCpuFlag(kCpuHasNEON)) {
526     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
527     if (IS_ALIGNED(dst_width, 4)) {
528       ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
529     }
530   }
531 #endif
532 #if defined(HAS_SCALEARGBFILTERCOLS_MSA)
533   if (filtering && TestCpuFlag(kCpuHasMSA)) {
534     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_MSA;
535     if (IS_ALIGNED(dst_width, 8)) {
536       ScaleARGBFilterCols = ScaleARGBFilterCols_MSA;
537     }
538   }
539 #endif
540 #if defined(HAS_SCALEARGBFILTERCOLS_LSX)
541   if (filtering && TestCpuFlag(kCpuHasLSX)) {
542     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_LSX;
543     if (IS_ALIGNED(dst_width, 8)) {
544       ScaleARGBFilterCols = ScaleARGBFilterCols_LSX;
545     }
546   }
547 #endif
548 #if defined(HAS_SCALEARGBCOLS_SSE2)
549   if (!filtering && TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
550     ScaleARGBFilterCols = ScaleARGBCols_SSE2;
551   }
552 #endif
553 #if defined(HAS_SCALEARGBCOLS_NEON)
554   if (!filtering && TestCpuFlag(kCpuHasNEON)) {
555     ScaleARGBFilterCols = ScaleARGBCols_Any_NEON;
556     if (IS_ALIGNED(dst_width, 8)) {
557       ScaleARGBFilterCols = ScaleARGBCols_NEON;
558     }
559   }
560 #endif
561 #if defined(HAS_SCALEARGBCOLS_MSA)
562   if (!filtering && TestCpuFlag(kCpuHasMSA)) {
563     ScaleARGBFilterCols = ScaleARGBCols_Any_MSA;
564     if (IS_ALIGNED(dst_width, 4)) {
565       ScaleARGBFilterCols = ScaleARGBCols_MSA;
566     }
567   }
568 #endif
569 #if defined(HAS_SCALEARGBCOLS_LSX)
570   if (!filtering && TestCpuFlag(kCpuHasLSX)) {
571     ScaleARGBFilterCols = ScaleARGBCols_Any_LSX;
572     if (IS_ALIGNED(dst_width, 4)) {
573       ScaleARGBFilterCols = ScaleARGBCols_LSX;
574     }
575   }
576 #endif
577   if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
578     ScaleARGBFilterCols = ScaleARGBColsUp2_C;
579 #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
580     if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
581       ScaleARGBFilterCols = ScaleARGBColsUp2_SSE2;
582     }
583 #endif
584   }
585 
586   if (y > max_y) {
587     y = max_y;
588   }
589 
590   {
591     int yi = y >> 16;
592     const uint8_t* src = src_argb + yi * (intptr_t)src_stride;
593 
594     // Allocate 2 rows of ARGB.
595     const int row_size = (dst_width * 4 + 31) & ~31;
596     align_buffer_64(row, row_size * 2);
597     if (!row)
598       return 1;
599 
600     uint8_t* rowptr = row;
601     int rowstride = row_size;
602     int lasty = yi;
603 
604     ScaleARGBFilterCols(rowptr, src, dst_width, x, dx);
605     if (src_height > 1) {
606       src += src_stride;
607     }
608     ScaleARGBFilterCols(rowptr + rowstride, src, dst_width, x, dx);
609     if (src_height > 2) {
610       src += src_stride;
611     }
612 
613     for (j = 0; j < dst_height; ++j) {
614       yi = y >> 16;
615       if (yi != lasty) {
616         if (y > max_y) {
617           y = max_y;
618           yi = y >> 16;
619           src = src_argb + yi * (intptr_t)src_stride;
620         }
621         if (yi != lasty) {
622           ScaleARGBFilterCols(rowptr, src, dst_width, x, dx);
623           rowptr += rowstride;
624           rowstride = -rowstride;
625           lasty = yi;
626           if ((y + 65536) < max_y) {
627             src += src_stride;
628           }
629         }
630       }
631       if (filtering == kFilterLinear) {
632         InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
633       } else {
634         int yf = (y >> 8) & 255;
635         InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
636       }
637       dst_argb += dst_stride;
638       y += dy;
639     }
640     free_aligned_buffer_64(row);
641   }
642   return 0;
643 }
644 
645 #ifdef YUVSCALEUP
646 // Scale YUV to ARGB up with bilinear interpolation.
ScaleYUVToARGBBilinearUp(int src_width,int src_height,int dst_width,int dst_height,int src_stride_y,int src_stride_u,int src_stride_v,int dst_stride_argb,const uint8_t * src_y,const uint8_t * src_u,const uint8_t * src_v,uint8_t * dst_argb,int x,int dx,int y,int dy,enum FilterMode filtering)647 static int ScaleYUVToARGBBilinearUp(int src_width,
648                                     int src_height,
649                                     int dst_width,
650                                     int dst_height,
651                                     int src_stride_y,
652                                     int src_stride_u,
653                                     int src_stride_v,
654                                     int dst_stride_argb,
655                                     const uint8_t* src_y,
656                                     const uint8_t* src_u,
657                                     const uint8_t* src_v,
658                                     uint8_t* dst_argb,
659                                     int x,
660                                     int dx,
661                                     int y,
662                                     int dy,
663                                     enum FilterMode filtering) {
664   int j;
665   void (*I422ToARGBRow)(const uint8_t* y_buf, const uint8_t* u_buf,
666                         const uint8_t* v_buf, uint8_t* rgb_buf, int width) =
667       I422ToARGBRow_C;
668 #if defined(HAS_I422TOARGBROW_SSSE3)
669   if (TestCpuFlag(kCpuHasSSSE3)) {
670     I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
671     if (IS_ALIGNED(src_width, 8)) {
672       I422ToARGBRow = I422ToARGBRow_SSSE3;
673     }
674   }
675 #endif
676 #if defined(HAS_I422TOARGBROW_AVX2)
677   if (TestCpuFlag(kCpuHasAVX2)) {
678     I422ToARGBRow = I422ToARGBRow_Any_AVX2;
679     if (IS_ALIGNED(src_width, 16)) {
680       I422ToARGBRow = I422ToARGBRow_AVX2;
681     }
682   }
683 #endif
684 #if defined(HAS_I422TOARGBROW_AVX512BW)
685   if (TestCpuFlag(kCpuHasAVX512BW | kCpuHasAVX512VL) ==
686       (kCpuHasAVX512BW | kCpuHasAVX512VL)) {
687     I422ToARGBRow = I422ToARGBRow_Any_AVX512BW;
688     if (IS_ALIGNED(src_width, 32)) {
689       I422ToARGBRow = I422ToARGBRow_AVX512BW;
690     }
691   }
692 #endif
693 #if defined(HAS_I422TOARGBROW_NEON)
694   if (TestCpuFlag(kCpuHasNEON)) {
695     I422ToARGBRow = I422ToARGBRow_Any_NEON;
696     if (IS_ALIGNED(src_width, 8)) {
697       I422ToARGBRow = I422ToARGBRow_NEON;
698     }
699   }
700 #endif
701 #if defined(HAS_I422TOARGBROW_MSA)
702   if (TestCpuFlag(kCpuHasMSA)) {
703     I422ToARGBRow = I422ToARGBRow_Any_MSA;
704     if (IS_ALIGNED(src_width, 8)) {
705       I422ToARGBRow = I422ToARGBRow_MSA;
706     }
707   }
708 #endif
709 #if defined(HAS_I422TOARGBROW_LSX)
710   if (TestCpuFlag(kCpuHasLSX)) {
711     I422ToARGBRow = I422ToARGBRow_Any_LSX;
712     if (IS_ALIGNED(src_width, 16)) {
713       I422ToARGBRow = I422ToARGBRow_LSX;
714     }
715   }
716 #endif
717 #if defined(HAS_I422TOARGBROW_LASX)
718   if (TestCpuFlag(kCpuHasLASX)) {
719     I422ToARGBRow = I422ToARGBRow_Any_LASX;
720     if (IS_ALIGNED(src_width, 32)) {
721       I422ToARGBRow = I422ToARGBRow_LASX;
722     }
723   }
724 #endif
725 #if defined(HAS_I422TOARGBROW_RVV)
726   if (TestCpuFlag(kCpuHasRVV)) {
727     I422ToARGBRow = I422ToARGBRow_RVV;
728   }
729 #endif
730 
731   void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb,
732                          ptrdiff_t src_stride, int dst_width,
733                          int source_y_fraction) = InterpolateRow_C;
734 #if defined(HAS_INTERPOLATEROW_SSSE3)
735   if (TestCpuFlag(kCpuHasSSSE3)) {
736     InterpolateRow = InterpolateRow_Any_SSSE3;
737     if (IS_ALIGNED(dst_width, 4)) {
738       InterpolateRow = InterpolateRow_SSSE3;
739     }
740   }
741 #endif
742 #if defined(HAS_INTERPOLATEROW_AVX2)
743   if (TestCpuFlag(kCpuHasAVX2)) {
744     InterpolateRow = InterpolateRow_Any_AVX2;
745     if (IS_ALIGNED(dst_width, 8)) {
746       InterpolateRow = InterpolateRow_AVX2;
747     }
748   }
749 #endif
750 #if defined(HAS_INTERPOLATEROW_NEON)
751   if (TestCpuFlag(kCpuHasNEON)) {
752     InterpolateRow = InterpolateRow_Any_NEON;
753     if (IS_ALIGNED(dst_width, 4)) {
754       InterpolateRow = InterpolateRow_NEON;
755     }
756   }
757 #endif
758 #if defined(HAS_INTERPOLATEROW_MSA)
759   if (TestCpuFlag(kCpuHasMSA)) {
760     InterpolateRow = InterpolateRow_Any_MSA;
761     if (IS_ALIGNED(dst_width, 8)) {
762       InterpolateRow = InterpolateRow_MSA;
763     }
764   }
765 #endif
766 #if defined(HAS_INTERPOLATEROW_LSX)
767   if (TestCpuFlag(kCpuHasLSX)) {
768     InterpolateRow = InterpolateRow_Any_LSX;
769     if (IS_ALIGNED(dst_width, 8)) {
770       InterpolateRow = InterpolateRow_LSX;
771     }
772   }
773 #endif
774 #if defined(HAS_INTERPOLATEROW_RVV)
775   if (TestCpuFlag(kCpuHasRVV)) {
776     InterpolateRow = InterpolateRow_RVV;
777   }
778 #endif
779 
780   void (*ScaleARGBFilterCols)(uint8_t* dst_argb, const uint8_t* src_argb,
781                               int dst_width, int x, int dx) =
782       filtering ? ScaleARGBFilterCols_C : ScaleARGBCols_C;
783   if (src_width >= 32768) {
784     ScaleARGBFilterCols =
785         filtering ? ScaleARGBFilterCols64_C : ScaleARGBCols64_C;
786   }
787 #if defined(HAS_SCALEARGBFILTERCOLS_SSSE3)
788   if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
789     ScaleARGBFilterCols = ScaleARGBFilterCols_SSSE3;
790   }
791 #endif
792 #if defined(HAS_SCALEARGBFILTERCOLS_NEON)
793   if (filtering && TestCpuFlag(kCpuHasNEON)) {
794     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_NEON;
795     if (IS_ALIGNED(dst_width, 4)) {
796       ScaleARGBFilterCols = ScaleARGBFilterCols_NEON;
797     }
798   }
799 #endif
800 #if defined(HAS_SCALEARGBFILTERCOLS_MSA)
801   if (filtering && TestCpuFlag(kCpuHasMSA)) {
802     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_MSA;
803     if (IS_ALIGNED(dst_width, 8)) {
804       ScaleARGBFilterCols = ScaleARGBFilterCols_MSA;
805     }
806   }
807 #endif
808 #if defined(HAS_SCALEARGBFILTERCOLS_LSX)
809   if (filtering && TestCpuFlag(kCpuHasLSX)) {
810     ScaleARGBFilterCols = ScaleARGBFilterCols_Any_LSX;
811     if (IS_ALIGNED(dst_width, 8)) {
812       ScaleARGBFilterCols = ScaleARGBFilterCols_LSX;
813     }
814   }
815 #endif
816 #if defined(HAS_SCALEARGBCOLS_SSE2)
817   if (!filtering && TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
818     ScaleARGBFilterCols = ScaleARGBCols_SSE2;
819   }
820 #endif
821 #if defined(HAS_SCALEARGBCOLS_NEON)
822   if (!filtering && TestCpuFlag(kCpuHasNEON)) {
823     ScaleARGBFilterCols = ScaleARGBCols_Any_NEON;
824     if (IS_ALIGNED(dst_width, 8)) {
825       ScaleARGBFilterCols = ScaleARGBCols_NEON;
826     }
827   }
828 #endif
829 #if defined(HAS_SCALEARGBCOLS_MSA)
830   if (!filtering && TestCpuFlag(kCpuHasMSA)) {
831     ScaleARGBFilterCols = ScaleARGBCols_Any_MSA;
832     if (IS_ALIGNED(dst_width, 4)) {
833       ScaleARGBFilterCols = ScaleARGBCols_MSA;
834     }
835   }
836 #endif
837 #if defined(HAS_SCALEARGBCOLS_LSX)
838   if (!filtering && TestCpuFlag(kCpuHasLSX)) {
839     ScaleARGBFilterCols = ScaleARGBCols_Any_LSX;
840     if (IS_ALIGNED(dst_width, 4)) {
841       ScaleARGBFilterCols = ScaleARGBCols_LSX;
842     }
843   }
844 #endif
845   if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
846     ScaleARGBFilterCols = ScaleARGBColsUp2_C;
847 #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
848     if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
849       ScaleARGBFilterCols = ScaleARGBColsUp2_SSE2;
850     }
851 #endif
852   }
853 
854   const int max_y = (src_height - 1) << 16;
855   if (y > max_y) {
856     y = max_y;
857   }
858   const int kYShift = 1;  // Shift Y by 1 to convert Y plane to UV coordinate.
859   int yi = y >> 16;
860   int uv_yi = yi >> kYShift;
861   const uint8_t* src_row_y = src_y + yi * (intptr_t)src_stride_y;
862   const uint8_t* src_row_u = src_u + uv_yi * (intptr_t)src_stride_u;
863   const uint8_t* src_row_v = src_v + uv_yi * (intptr_t)src_stride_v;
864 
865   // Allocate 1 row of ARGB for source conversion and 2 rows of ARGB
866   // scaled horizontally to the destination width.
867   const int row_size = (dst_width * 4 + 31) & ~31;
868   align_buffer_64(row, row_size * 2 + src_width * 4);
869 
870   uint8_t* argb_row = row + row_size * 2;
871   uint8_t* rowptr = row;
872   int rowstride = row_size;
873   int lasty = yi;
874   if (!row)
875     return 1;
876 
877   // TODO(fbarchard): Convert first 2 rows of YUV to ARGB.
878   ScaleARGBFilterCols(rowptr, src_row_y, dst_width, x, dx);
879   if (src_height > 1) {
880     src_row_y += src_stride_y;
881     if (yi & 1) {
882       src_row_u += src_stride_u;
883       src_row_v += src_stride_v;
884     }
885   }
886   ScaleARGBFilterCols(rowptr + rowstride, src_row_y, dst_width, x, dx);
887   if (src_height > 2) {
888     src_row_y += src_stride_y;
889     if (!(yi & 1)) {
890       src_row_u += src_stride_u;
891       src_row_v += src_stride_v;
892     }
893   }
894 
895   for (j = 0; j < dst_height; ++j) {
896     yi = y >> 16;
897     if (yi != lasty) {
898       if (y > max_y) {
899         y = max_y;
900         yi = y >> 16;
901         uv_yi = yi >> kYShift;
902         src_row_y = src_y + yi * (intptr_t)src_stride_y;
903         src_row_u = src_u + uv_yi * (intptr_t)src_stride_u;
904         src_row_v = src_v + uv_yi * (intptr_t)src_stride_v;
905       }
906       if (yi != lasty) {
907         // TODO(fbarchard): Convert the clipped region of row.
908         I422ToARGBRow(src_row_y, src_row_u, src_row_v, argb_row, src_width);
909         ScaleARGBFilterCols(rowptr, argb_row, dst_width, x, dx);
910         rowptr += rowstride;
911         rowstride = -rowstride;
912         lasty = yi;
913         src_row_y += src_stride_y;
914         if (yi & 1) {
915           src_row_u += src_stride_u;
916           src_row_v += src_stride_v;
917         }
918       }
919     }
920     if (filtering == kFilterLinear) {
921       InterpolateRow(dst_argb, rowptr, 0, dst_width * 4, 0);
922     } else {
923       int yf = (y >> 8) & 255;
924       InterpolateRow(dst_argb, rowptr, rowstride, dst_width * 4, yf);
925     }
926     dst_argb += dst_stride_argb;
927     y += dy;
928   }
929   free_aligned_buffer_64(row);
930   return 0;
931 }
932 #endif
933 
934 // Scale ARGB to/from any dimensions, without interpolation.
935 // Fixed point math is used for performance: The upper 16 bits
936 // of x and dx is the integer part of the source position and
937 // the lower 16 bits are the fixed decimal part.
938 
ScaleARGBSimple(int src_width,int src_height,int dst_width,int dst_height,int src_stride,int dst_stride,const uint8_t * src_argb,uint8_t * dst_argb,int x,int dx,int y,int dy)939 static void ScaleARGBSimple(int src_width,
940                             int src_height,
941                             int dst_width,
942                             int dst_height,
943                             int src_stride,
944                             int dst_stride,
945                             const uint8_t* src_argb,
946                             uint8_t* dst_argb,
947                             int x,
948                             int dx,
949                             int y,
950                             int dy) {
951   int j;
952   void (*ScaleARGBCols)(uint8_t* dst_argb, const uint8_t* src_argb,
953                         int dst_width, int x, int dx) =
954       (src_width >= 32768) ? ScaleARGBCols64_C : ScaleARGBCols_C;
955   (void)src_height;
956 #if defined(HAS_SCALEARGBCOLS_SSE2)
957   if (TestCpuFlag(kCpuHasSSE2) && src_width < 32768) {
958     ScaleARGBCols = ScaleARGBCols_SSE2;
959   }
960 #endif
961 #if defined(HAS_SCALEARGBCOLS_NEON)
962   if (TestCpuFlag(kCpuHasNEON)) {
963     ScaleARGBCols = ScaleARGBCols_Any_NEON;
964     if (IS_ALIGNED(dst_width, 8)) {
965       ScaleARGBCols = ScaleARGBCols_NEON;
966     }
967   }
968 #endif
969 #if defined(HAS_SCALEARGBCOLS_MSA)
970   if (TestCpuFlag(kCpuHasMSA)) {
971     ScaleARGBCols = ScaleARGBCols_Any_MSA;
972     if (IS_ALIGNED(dst_width, 4)) {
973       ScaleARGBCols = ScaleARGBCols_MSA;
974     }
975   }
976 #endif
977 #if defined(HAS_SCALEARGBCOLS_LSX)
978   if (TestCpuFlag(kCpuHasLSX)) {
979     ScaleARGBCols = ScaleARGBCols_Any_LSX;
980     if (IS_ALIGNED(dst_width, 4)) {
981       ScaleARGBCols = ScaleARGBCols_LSX;
982     }
983   }
984 #endif
985   if (src_width * 2 == dst_width && x < 0x8000) {
986     ScaleARGBCols = ScaleARGBColsUp2_C;
987 #if defined(HAS_SCALEARGBCOLSUP2_SSE2)
988     if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
989       ScaleARGBCols = ScaleARGBColsUp2_SSE2;
990     }
991 #endif
992   }
993 
994   for (j = 0; j < dst_height; ++j) {
995     ScaleARGBCols(dst_argb, src_argb + (y >> 16) * (intptr_t)src_stride,
996                   dst_width, x, dx);
997     dst_argb += dst_stride;
998     y += dy;
999   }
1000 }
1001 
1002 // ScaleARGB a ARGB.
1003 // This function in turn calls a scaling function
1004 // suitable for handling the desired resolutions.
ScaleARGB(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)1005 static int ScaleARGB(const uint8_t* src,
1006                      int src_stride,
1007                      int src_width,
1008                      int src_height,
1009                      uint8_t* dst,
1010                      int dst_stride,
1011                      int dst_width,
1012                      int dst_height,
1013                      int clip_x,
1014                      int clip_y,
1015                      int clip_width,
1016                      int clip_height,
1017                      enum FilterMode filtering) {
1018   // Initial source x/y coordinate and step values as 16.16 fixed point.
1019   int x = 0;
1020   int y = 0;
1021   int dx = 0;
1022   int dy = 0;
1023   // ARGB does not support box filter yet, but allow the user to pass it.
1024   // Simplify filtering when possible.
1025   filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
1026                                 filtering);
1027 
1028   // Negative src_height means invert the image.
1029   if (src_height < 0) {
1030     src_height = -src_height;
1031     src = src + (src_height - 1) * (intptr_t)src_stride;
1032     src_stride = -src_stride;
1033   }
1034   ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
1035              &dx, &dy);
1036   src_width = Abs(src_width);
1037   if (clip_x) {
1038     int64_t clipf = (int64_t)(clip_x)*dx;
1039     x += (clipf & 0xffff);
1040     src += (clipf >> 16) * 4;
1041     dst += clip_x * 4;
1042   }
1043   if (clip_y) {
1044     int64_t clipf = (int64_t)(clip_y)*dy;
1045     y += (clipf & 0xffff);
1046     src += (clipf >> 16) * (intptr_t)src_stride;
1047     dst += clip_y * dst_stride;
1048   }
1049 
1050   // Special case for integer step values.
1051   if (((dx | dy) & 0xffff) == 0) {
1052     if (!dx || !dy) {  // 1 pixel wide and/or tall.
1053       filtering = kFilterNone;
1054     } else {
1055       // Optimized even scale down. ie 2, 4, 6, 8, 10x.
1056       if (!(dx & 0x10000) && !(dy & 0x10000)) {
1057         if (dx == 0x20000) {
1058           // Optimized 1/2 downsample.
1059           ScaleARGBDown2(src_width, src_height, clip_width, clip_height,
1060                          src_stride, dst_stride, src, dst, x, dx, y, dy,
1061                          filtering);
1062           return 0;
1063         }
1064         if (dx == 0x40000 && filtering == kFilterBox) {
1065           // Optimized 1/4 box downsample.
1066           return ScaleARGBDown4Box(src_width, src_height, clip_width,
1067                                    clip_height, src_stride, dst_stride, src,
1068                                    dst, x, dx, y, dy);
1069         }
1070         ScaleARGBDownEven(src_width, src_height, clip_width, clip_height,
1071                           src_stride, dst_stride, src, dst, x, dx, y, dy,
1072                           filtering);
1073         return 0;
1074       }
1075       // Optimized odd scale down. ie 3, 5, 7, 9x.
1076       if ((dx & 0x10000) && (dy & 0x10000)) {
1077         filtering = kFilterNone;
1078         if (dx == 0x10000 && dy == 0x10000) {
1079           // Straight copy.
1080           ARGBCopy(src + (y >> 16) * (intptr_t)src_stride + (x >> 16) * 4,
1081                    src_stride, dst, dst_stride, clip_width, clip_height);
1082           return 0;
1083         }
1084       }
1085     }
1086   }
1087   if (dx == 0x10000 && (x & 0xffff) == 0) {
1088     // Arbitrary scale vertically, but unscaled horizontally.
1089     ScalePlaneVertical(src_height, clip_width, clip_height, src_stride,
1090                        dst_stride, src, dst, x, y, dy, /*bpp=*/4, filtering);
1091     return 0;
1092   }
1093   if (filtering && dy < 65536) {
1094     return ScaleARGBBilinearUp(src_width, src_height, clip_width, clip_height,
1095                                src_stride, dst_stride, src, dst, x, dx, y, dy,
1096                                filtering);
1097   }
1098   if (filtering) {
1099     return ScaleARGBBilinearDown(src_width, src_height, clip_width, clip_height,
1100                                  src_stride, dst_stride, src, dst, x, dx, y, dy,
1101                                  filtering);
1102   }
1103   ScaleARGBSimple(src_width, src_height, clip_width, clip_height, src_stride,
1104                   dst_stride, src, dst, x, dx, y, dy);
1105   return 0;
1106 }
1107 
1108 LIBYUV_API
ARGBScaleClip(const uint8_t * src_argb,int src_stride_argb,int src_width,int src_height,uint8_t * dst_argb,int dst_stride_argb,int dst_width,int dst_height,int clip_x,int clip_y,int clip_width,int clip_height,enum FilterMode filtering)1109 int ARGBScaleClip(const uint8_t* src_argb,
1110                   int src_stride_argb,
1111                   int src_width,
1112                   int src_height,
1113                   uint8_t* dst_argb,
1114                   int dst_stride_argb,
1115                   int dst_width,
1116                   int dst_height,
1117                   int clip_x,
1118                   int clip_y,
1119                   int clip_width,
1120                   int clip_height,
1121                   enum FilterMode filtering) {
1122   if (!src_argb || src_width == 0 || src_height == 0 || !dst_argb ||
1123       dst_width <= 0 || dst_height <= 0 || clip_x < 0 || clip_y < 0 ||
1124       clip_width > 32768 || clip_height > 32768 ||
1125       (clip_x + clip_width) > dst_width ||
1126       (clip_y + clip_height) > dst_height) {
1127     return -1;
1128   }
1129   return ScaleARGB(src_argb, src_stride_argb, src_width, src_height, dst_argb,
1130                    dst_stride_argb, dst_width, dst_height, clip_x, clip_y,
1131                    clip_width, clip_height, filtering);
1132 }
1133 
1134 // Scale an ARGB image.
1135 LIBYUV_API
ARGBScale(const uint8_t * src_argb,int src_stride_argb,int src_width,int src_height,uint8_t * dst_argb,int dst_stride_argb,int dst_width,int dst_height,enum FilterMode filtering)1136 int ARGBScale(const uint8_t* src_argb,
1137               int src_stride_argb,
1138               int src_width,
1139               int src_height,
1140               uint8_t* dst_argb,
1141               int dst_stride_argb,
1142               int dst_width,
1143               int dst_height,
1144               enum FilterMode filtering) {
1145   if (!src_argb || src_width == 0 || src_height == 0 || src_width > 32768 ||
1146       src_height > 32768 || !dst_argb || dst_width <= 0 || dst_height <= 0) {
1147     return -1;
1148   }
1149   return ScaleARGB(src_argb, src_stride_argb, src_width, src_height, dst_argb,
1150                    dst_stride_argb, dst_width, dst_height, 0, 0, dst_width,
1151                    dst_height, filtering);
1152 }
1153 
1154 // Scale with YUV conversion to ARGB and clipping.
1155 LIBYUV_API
YUVToARGBScaleClip(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint32_t src_fourcc,int src_width,int src_height,uint8_t * dst_argb,int dst_stride_argb,uint32_t dst_fourcc,int dst_width,int dst_height,int clip_x,int clip_y,int clip_width,int clip_height,enum FilterMode filtering)1156 int YUVToARGBScaleClip(const uint8_t* src_y,
1157                        int src_stride_y,
1158                        const uint8_t* src_u,
1159                        int src_stride_u,
1160                        const uint8_t* src_v,
1161                        int src_stride_v,
1162                        uint32_t src_fourcc,
1163                        int src_width,
1164                        int src_height,
1165                        uint8_t* dst_argb,
1166                        int dst_stride_argb,
1167                        uint32_t dst_fourcc,
1168                        int dst_width,
1169                        int dst_height,
1170                        int clip_x,
1171                        int clip_y,
1172                        int clip_width,
1173                        int clip_height,
1174                        enum FilterMode filtering) {
1175   int r;
1176   uint8_t* argb_buffer = (uint8_t*)malloc(src_width * src_height * 4);
1177   if (!argb_buffer) {
1178     return 1;  // Out of memory runtime error.
1179   }
1180   (void)src_fourcc;  // TODO(fbarchard): implement and/or assert.
1181   (void)dst_fourcc;
1182   I420ToARGB(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
1183              argb_buffer, src_width * 4, src_width, src_height);
1184 
1185   r = ARGBScaleClip(argb_buffer, src_width * 4, src_width, src_height, dst_argb,
1186                     dst_stride_argb, dst_width, dst_height, clip_x, clip_y,
1187                     clip_width, clip_height, filtering);
1188   free(argb_buffer);
1189   return r;
1190 }
1191 
1192 #ifdef __cplusplus
1193 }  // extern "C"
1194 }  // namespace libyuv
1195 #endif
1196