Coverage Report

Created: 2026-10-27 06:32

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