Coverage Report

Created: 2026-10-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/third_party/libyuv/source/scale.c
Line
Count
Source
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/planar_functions.h"  // For CopyPlane
17
#include "libyuv/row.h"
18
#include "libyuv/scale_row.h"
19
20
4.00k
static __inline int Abs(int v) {
21
4.00k
  return v >= 0 ? v : -v;
22
4.00k
}
23
24
419
#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s)
25
26
340k
#define MIN1(x) ((x) < 1 ? 1 : (x))
27
28
108k
static __inline uint32_t SumPixels(int iboxwidth, const uint16_t* src_ptr) {
29
108k
  uint32_t sum = 0u;
30
108k
  int x;
31
108k
  assert(iboxwidth > 0);
32
471k
  for (x = 0; x < iboxwidth; ++x) {
33
363k
    sum += src_ptr[x];
34
363k
  }
35
108k
  return sum;
36
108k
}
37
38
229k
static __inline uint32_t SumPixels_16(int iboxwidth, const uint32_t* src_ptr) {
39
229k
  uint32_t sum = 0u;
40
229k
  int x;
41
229k
  assert(iboxwidth > 0);
42
1.05M
  for (x = 0; x < iboxwidth; ++x) {
43
829k
    sum += src_ptr[x];
44
829k
  }
45
229k
  return sum;
46
229k
}
47
48
static void ScaleAddCols2_C(int dst_width,
49
                            int boxheight,
50
                            int x,
51
                            int dx,
52
                            const uint16_t* src_ptr,
53
2.62k
                            uint8_t* dst_ptr) {
54
2.62k
  int i;
55
2.62k
  int scaletbl[2];
56
2.62k
  int minboxwidth = dx >> 16;
57
2.62k
  int boxwidth;
58
2.62k
  scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight);
59
2.62k
  scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight);
60
87.6k
  for (i = 0; i < dst_width; ++i) {
61
85.0k
    int ix = x >> 16;
62
85.0k
    x += dx;
63
85.0k
    boxwidth = MIN1((x >> 16) - ix);
64
85.0k
    int scaletbl_index = boxwidth - minboxwidth;
65
85.0k
    assert((scaletbl_index == 0) || (scaletbl_index == 1));
66
85.0k
    *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + ix) *
67
85.0k
                               scaletbl[scaletbl_index] >>
68
85.0k
                           16);
69
85.0k
  }
70
2.62k
}
71
72
static void ScaleAddCols2_16_C(int dst_width,
73
                               int boxheight,
74
                               int x,
75
                               int dx,
76
                               const uint32_t* src_ptr,
77
5.88k
                               uint16_t* dst_ptr) {
78
5.88k
  int i;
79
5.88k
  int scaletbl[2];
80
5.88k
  int minboxwidth = dx >> 16;
81
5.88k
  int boxwidth;
82
5.88k
  scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight);
83
5.88k
  scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight);
84
232k
  for (i = 0; i < dst_width; ++i) {
85
226k
    int ix = x >> 16;
86
226k
    x += dx;
87
226k
    boxwidth = MIN1((x >> 16) - ix);
88
226k
    int scaletbl_index = boxwidth - minboxwidth;
89
226k
    assert((scaletbl_index == 0) || (scaletbl_index == 1));
90
226k
    *dst_ptr++ =
91
226k
        SumPixels_16(boxwidth, src_ptr + ix) * scaletbl[scaletbl_index] >> 16;
92
226k
  }
93
5.88k
}
94
95
static void ScaleAddCols0_C(int dst_width,
96
                            int boxheight,
97
                            int x,
98
                            int dx,
99
                            const uint16_t* src_ptr,
100
0
                            uint8_t* dst_ptr) {
101
0
  int scaleval = 65536 / boxheight;
102
0
  int i;
103
0
  (void)dx;
104
0
  src_ptr += (x >> 16);
105
0
  for (i = 0; i < dst_width; ++i) {
106
0
    *dst_ptr++ = (uint8_t)(src_ptr[i] * scaleval >> 16);
107
0
  }
108
0
}
109
110
static void ScaleAddCols1_C(int dst_width,
111
                            int boxheight,
112
                            int x,
113
                            int dx,
114
                            const uint16_t* src_ptr,
115
1.16k
                            uint8_t* dst_ptr) {
116
1.16k
  int boxwidth = MIN1(dx >> 16);
117
1.16k
  int scaleval = 65536 / (boxwidth * boxheight);
118
1.16k
  int i;
119
1.16k
  x >>= 16;
120
24.4k
  for (i = 0; i < dst_width; ++i) {
121
23.2k
    *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + x) * scaleval >> 16);
122
23.2k
    x += boxwidth;
123
23.2k
  }
124
1.16k
}
125
126
static void ScaleAddCols1_16_C(int dst_width,
127
                               int boxheight,
128
                               int x,
129
                               int dx,
130
                               const uint32_t* src_ptr,
131
342
                               uint16_t* dst_ptr) {
132
342
  int boxwidth = MIN1(dx >> 16);
133
342
  int scaleval = 65536 / (boxwidth * boxheight);
134
342
  int i;
135
3.37k
  for (i = 0; i < dst_width; ++i) {
136
3.02k
    *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + x) * scaleval >> 16;
137
3.02k
    x += boxwidth;
138
3.02k
  }
139
342
}
140
141
// Scale plane down to any dimensions, with interpolation.
142
// (boxfilter).
143
//
144
// Same method as SimpleScale, which is fixed point, outputting
145
// one pixel of destination using fixed point (16.16) to step
146
// through source, sampling a box of pixel with simple
147
// averaging.
148
static int ScalePlaneBox(int src_width,
149
                         int src_height,
150
                         int dst_width,
151
                         int dst_height,
152
                         int src_stride,
153
                         int dst_stride,
154
                         const uint8_t* src_ptr,
155
272
                         uint8_t* dst_ptr) {
156
272
  int j, k;
157
  // Initial source x/y coordinate and step values as 16.16 fixed point.
158
272
  int x = 0;
159
272
  int y = 0;
160
272
  int dx = 0;
161
272
  int dy = 0;
162
272
  const int max_y = (src_height << 16);
163
272
  ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y,
164
272
             &dx, &dy);
165
272
  src_width = Abs(src_width);
166
272
  {
167
    // Allocate a row buffer of uint16_t.
168
272
    align_buffer_64(row16, src_width * 2);
169
272
    if (!row16)
170
0
      return 1;
171
272
    void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx,
172
272
                         const uint16_t* src_ptr, uint8_t* dst_ptr) =
173
272
        (dx & 0xffff) ? ScaleAddCols2_C
174
272
                      : ((dx != 0x10000) ? ScaleAddCols1_C : ScaleAddCols0_C);
175
272
    void (*ScaleAddRow)(const uint8_t* src_ptr, uint16_t* dst_ptr,
176
272
                        int src_width) = ScaleAddRow_C;
177
178
4.06k
    for (j = 0; j < dst_height; ++j) {
179
3.78k
      int boxheight;
180
3.78k
      int iy = y >> 16;
181
3.78k
      const uint8_t* src = src_ptr + iy * (int64_t)src_stride;
182
3.78k
      y += dy;
183
3.78k
      if (y > max_y) {
184
0
        y = max_y;
185
0
      }
186
3.78k
      boxheight = MIN1((y >> 16) - iy);
187
3.78k
      memset(row16, 0, src_width * 2);
188
37.6k
      for (k = 0; k < boxheight; ++k) {
189
33.9k
        ScaleAddRow(src, (uint16_t*)(row16), src_width);
190
33.9k
        src += src_stride;
191
33.9k
      }
192
3.78k
      ScaleAddCols(dst_width, boxheight, x, dx, (uint16_t*)(row16), dst_ptr);
193
3.78k
      dst_ptr += dst_stride;
194
3.78k
    }
195
272
    free_aligned_buffer_64(row16);
196
272
  }
197
0
  return 0;
198
272
}
199
200
static int ScalePlaneBox_16(int src_width,
201
                            int src_height,
202
                            int dst_width,
203
                            int dst_height,
204
                            int src_stride,
205
                            int dst_stride,
206
                            const uint16_t* src_ptr,
207
163
                            uint16_t* dst_ptr) {
208
163
  int j, k;
209
  // Initial source x/y coordinate and step values as 16.16 fixed point.
210
163
  int x = 0;
211
163
  int y = 0;
212
163
  int dx = 0;
213
163
  int dy = 0;
214
163
  const int max_y = (src_height << 16);
215
163
  ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y,
216
163
             &dx, &dy);
217
163
  src_width = Abs(src_width);
218
163
  {
219
    // Allocate a row buffer of uint32_t.
220
163
    align_buffer_64(row32, src_width * 4);
221
163
    if (!row32)
222
0
      return 1;
223
163
    void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx,
224
163
                         const uint32_t* src_ptr, uint16_t* dst_ptr) =
225
163
        (dx & 0xffff) ? ScaleAddCols2_16_C : ScaleAddCols1_16_C;
226
163
    void (*ScaleAddRow)(const uint16_t* src_ptr, uint32_t* dst_ptr,
227
163
                        int src_width) = ScaleAddRow_16_C;
228
229
#if defined(HAS_SCALEADDROW_16_SSE2)
230
    if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(src_width, 16)) {
231
      ScaleAddRow = ScaleAddRow_16_SSE2;
232
    }
233
#endif
234
235
6.39k
    for (j = 0; j < dst_height; ++j) {
236
6.23k
      int boxheight;
237
6.23k
      int iy = y >> 16;
238
6.23k
      const uint16_t* src = src_ptr + iy * (int64_t)src_stride;
239
6.23k
      y += dy;
240
6.23k
      if (y > max_y) {
241
0
        y = max_y;
242
0
      }
243
6.23k
      boxheight = MIN1((y >> 16) - iy);
244
6.23k
      memset(row32, 0, src_width * 4);
245
48.1k
      for (k = 0; k < boxheight; ++k) {
246
41.8k
        ScaleAddRow(src, (uint32_t*)(row32), src_width);
247
41.8k
        src += src_stride;
248
41.8k
      }
249
6.23k
      ScaleAddCols(dst_width, boxheight, x, dx, (uint32_t*)(row32), dst_ptr);
250
6.23k
      dst_ptr += dst_stride;
251
6.23k
    }
252
163
    free_aligned_buffer_64(row32);
253
163
  }
254
0
  return 0;
255
163
}
256
257
// Scale plane down with bilinear interpolation.
258
static int ScalePlaneBilinearDown(int src_width,
259
                                  int src_height,
260
                                  int dst_width,
261
                                  int dst_height,
262
                                  int src_stride,
263
                                  int dst_stride,
264
                                  const uint8_t* src_ptr,
265
                                  uint8_t* dst_ptr,
266
401
                                  enum FilterMode filtering) {
267
  // Initial source x/y coordinate and step values as 16.16 fixed point.
268
401
  int x = 0;
269
401
  int y = 0;
270
401
  int dx = 0;
271
401
  int dy = 0;
272
  // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
273
  // Allocate a row buffer.
274
401
  align_buffer_64(row, src_width);
275
401
  if (!row)
276
0
    return 1;
277
278
401
  const int max_y = (src_height - 1) << 16;
279
401
  int j;
280
401
  void (*ScaleFilterCols)(uint8_t* dst_ptr, const uint8_t* src_ptr,
281
401
                          int dst_width, int x, int dx) =
282
401
      (src_width >= 32768) ? ScaleFilterCols64_C : ScaleFilterCols_C;
283
401
  void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr,
284
401
                         ptrdiff_t src_stride, int dst_width,
285
401
                         int source_y_fraction) = InterpolateRow_C;
286
401
  ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
287
401
             &dx, &dy);
288
401
  src_width = Abs(src_width);
289
290
401
  if (y > max_y) {
291
24
    y = max_y;
292
24
  }
293
294
44.3k
  for (j = 0; j < dst_height; ++j) {
295
43.9k
    int yi = y >> 16;
296
43.9k
    const uint8_t* src = src_ptr + yi * (int64_t)src_stride;
297
43.9k
    if (filtering == kFilterLinear) {
298
6.46k
      ScaleFilterCols(dst_ptr, src, dst_width, x, dx);
299
37.4k
    } else {
300
37.4k
      int yf = (y >> 8) & 255;
301
37.4k
      InterpolateRow(row, src, src_stride, src_width, yf);
302
37.4k
      ScaleFilterCols(dst_ptr, row, dst_width, x, dx);
303
37.4k
    }
304
43.9k
    dst_ptr += dst_stride;
305
43.9k
    y += dy;
306
43.9k
    if (y > max_y) {
307
464
      y = max_y;
308
464
    }
309
43.9k
  }
310
401
  free_aligned_buffer_64(row);
311
401
  return 0;
312
401
}
313
314
static int ScalePlaneBilinearDown_16(int src_width,
315
                                     int src_height,
316
                                     int dst_width,
317
                                     int dst_height,
318
                                     int src_stride,
319
                                     int dst_stride,
320
                                     const uint16_t* src_ptr,
321
                                     uint16_t* dst_ptr,
322
536
                                     enum FilterMode filtering) {
323
  // Initial source x/y coordinate and step values as 16.16 fixed point.
324
536
  int x = 0;
325
536
  int y = 0;
326
536
  int dx = 0;
327
536
  int dy = 0;
328
  // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
329
  // Allocate a row buffer.
330
536
  align_buffer_64(row, src_width * 2);
331
536
  if (!row)
332
0
    return 1;
333
334
536
  const int max_y = (src_height - 1) << 16;
335
536
  int j;
336
536
  void (*ScaleFilterCols)(uint16_t* dst_ptr, const uint16_t* src_ptr,
337
536
                          int dst_width, int x, int dx) =
338
536
      (src_width >= 32768) ? ScaleFilterCols64_16_C : ScaleFilterCols_16_C;
339
536
  void (*InterpolateRow)(uint16_t* dst_ptr, const uint16_t* src_ptr,
340
536
                         ptrdiff_t src_stride, int dst_width,
341
536
                         int source_y_fraction) = InterpolateRow_16_C;
342
536
  ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
343
536
             &dx, &dy);
344
536
  src_width = Abs(src_width);
345
346
536
  if (y > max_y) {
347
7
    y = max_y;
348
7
  }
349
350
31.9k
  for (j = 0; j < dst_height; ++j) {
351
31.4k
    int yi = y >> 16;
352
31.4k
    const uint16_t* src = src_ptr + yi * (int64_t)src_stride;
353
31.4k
    if (filtering == kFilterLinear) {
354
656
      ScaleFilterCols(dst_ptr, src, dst_width, x, dx);
355
30.7k
    } else {
356
30.7k
      int yf = (y >> 8) & 255;
357
30.7k
      InterpolateRow((uint16_t*)row, src, src_stride, src_width, yf);
358
30.7k
      ScaleFilterCols(dst_ptr, (uint16_t*)row, dst_width, x, dx);
359
30.7k
    }
360
31.4k
    dst_ptr += dst_stride;
361
31.4k
    y += dy;
362
31.4k
    if (y > max_y) {
363
549
      y = max_y;
364
549
    }
365
31.4k
  }
366
536
  free_aligned_buffer_64(row);
367
536
  return 0;
368
536
}
369
370
// Scale up down with bilinear interpolation.
371
static int ScalePlaneBilinearUp(int src_width,
372
                                int src_height,
373
                                int dst_width,
374
                                int dst_height,
375
                                int src_stride,
376
                                int dst_stride,
377
                                const uint8_t* src_ptr,
378
                                uint8_t* dst_ptr,
379
569
                                enum FilterMode filtering) {
380
569
  int j;
381
  // Initial source x/y coordinate and step values as 16.16 fixed point.
382
569
  int x = 0;
383
569
  int y = 0;
384
569
  int dx = 0;
385
569
  int dy = 0;
386
569
  const int max_y = (src_height - 1) << 16;
387
569
  void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr,
388
569
                         ptrdiff_t src_stride, int dst_width,
389
569
                         int source_y_fraction) = InterpolateRow_C;
390
569
  void (*ScaleFilterCols)(uint8_t* dst_ptr, const uint8_t* src_ptr,
391
569
                          int dst_width, int x, int dx) =
392
569
      filtering ? ScaleFilterCols_C : ScaleCols_C;
393
569
  ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
394
569
             &dx, &dy);
395
569
  src_width = Abs(src_width);
396
397
569
  if (filtering && src_width >= 32768) {
398
0
    ScaleFilterCols = ScaleFilterCols64_C;
399
0
  }
400
569
  if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
401
0
    ScaleFilterCols = ScaleColsUp2_C;
402
0
  }
403
404
569
  if (y > max_y) {
405
91
    y = max_y;
406
91
  }
407
569
  {
408
569
    int yi = y >> 16;
409
569
    const uint8_t* src = src_ptr + yi * (int64_t)src_stride;
410
411
    // Allocate 2 row buffers.
412
569
    const int row_size = (dst_width + 31) & ~31;
413
569
    align_buffer_64(row, row_size * 2);
414
569
    if (!row)
415
0
      return 1;
416
417
569
    uint8_t* rowptr = row;
418
569
    int rowstride = row_size;
419
569
    int lasty = yi;
420
421
569
    ScaleFilterCols(rowptr, src, dst_width, x, dx);
422
569
    if (src_height > 1) {
423
472
      src += src_stride;
424
472
    }
425
569
    ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx);
426
569
    if (src_height > 2) {
427
435
      src += src_stride;
428
435
    }
429
430
6.07M
    for (j = 0; j < dst_height; ++j) {
431
6.07M
      yi = y >> 16;
432
6.07M
      if (yi != lasty) {
433
78.9k
        if (y > max_y) {
434
0
          y = max_y;
435
0
          yi = y >> 16;
436
0
          src = src_ptr + yi * (int64_t)src_stride;
437
0
        }
438
78.9k
        if (yi != lasty) {
439
78.9k
          ScaleFilterCols(rowptr, src, dst_width, x, dx);
440
78.9k
          rowptr += rowstride;
441
78.9k
          rowstride = -rowstride;
442
78.9k
          lasty = yi;
443
78.9k
          if ((y + 65536) < max_y) {
444
78.5k
            src += src_stride;
445
78.5k
          }
446
78.9k
        }
447
78.9k
      }
448
6.07M
      if (filtering == kFilterLinear) {
449
366k
        InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0);
450
5.71M
      } else {
451
5.71M
        int yf = (y >> 8) & 255;
452
5.71M
        InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf);
453
5.71M
      }
454
6.07M
      dst_ptr += dst_stride;
455
6.07M
      y += dy;
456
6.07M
    }
457
569
    free_aligned_buffer_64(row);
458
569
  }
459
0
  return 0;
460
569
}
461
462
// Scale plane, horizontally up by 2 times.
463
// Uses linear filter horizontally, nearest vertically.
464
// This is an optimized version for scaling up a plane to 2 times of
465
// its original width, using linear interpolation.
466
// This is used to scale U and V planes of I422 to I444.
467
static void ScalePlaneUp2_Linear(int src_width,
468
                                 int src_height,
469
                                 int dst_width,
470
                                 int dst_height,
471
                                 int src_stride,
472
                                 int dst_stride,
473
                                 const uint8_t* src_ptr,
474
54
                                 uint8_t* dst_ptr) {
475
54
  void (*ScaleRowUp)(const uint8_t* src_ptr, uint8_t* dst_ptr, int dst_width) =
476
54
      ScaleRowUp2_Linear_Any_C;
477
54
  int i;
478
54
  int y;
479
54
  int dy;
480
481
54
  (void)src_width;
482
  // This function can only scale up by 2 times horizontally.
483
54
  assert(src_width == ((dst_width + 1) / 2));
484
485
54
  if (dst_height == 1) {
486
9
    ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr,
487
9
               dst_width);
488
45
  } else {
489
45
    dy = FixedDiv(src_height - 1, dst_height - 1);
490
45
    y = (1 << 15) - 1;
491
40.6k
    for (i = 0; i < dst_height; ++i) {
492
40.5k
      ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width);
493
40.5k
      dst_ptr += dst_stride;
494
40.5k
      y += dy;
495
40.5k
    }
496
45
  }
497
54
}
498
499
// Scale plane, up by 2 times.
500
// This is an optimized version for scaling up a plane to 2 times of
501
// its original size, using bilinear interpolation.
502
// This is used to scale U and V planes of I420 to I444.
503
static void ScalePlaneUp2_Bilinear(int src_width,
504
                                   int src_height,
505
                                   int dst_width,
506
                                   int dst_height,
507
                                   int src_stride,
508
                                   int dst_stride,
509
                                   const uint8_t* src_ptr,
510
34
                                   uint8_t* dst_ptr) {
511
34
  void (*Scale2RowUp)(const uint8_t* src_ptr, ptrdiff_t src_stride,
512
34
                      uint8_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) =
513
34
      ScaleRowUp2_Bilinear_Any_C;
514
34
  int x;
515
516
34
  (void)src_width;
517
  // This function can only scale up by 2 times.
518
34
  assert(src_width == ((dst_width + 1) / 2));
519
34
  assert(src_height == ((dst_height + 1) / 2));
520
521
34
  Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
522
34
  dst_ptr += dst_stride;
523
306
  for (x = 0; x < src_height - 1; ++x) {
524
272
    Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width);
525
272
    src_ptr += src_stride;
526
    // TODO(fbarchard): Test performance of writing one row of destination at a
527
    // time.
528
272
    dst_ptr += 2 * dst_stride;
529
272
  }
530
34
  if (!(dst_height & 1)) {
531
12
    Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
532
12
  }
533
34
}
534
535
// Scale at most 14 bit plane, horizontally up by 2 times.
536
// This is an optimized version for scaling up a plane to 2 times of
537
// its original width, using linear interpolation.
538
// stride is in count of uint16_t.
539
// This is used to scale U and V planes of I210 to I410 and I212 to I412.
540
static void ScalePlaneUp2_12_Linear(int src_width,
541
                                    int src_height,
542
                                    int dst_width,
543
                                    int dst_height,
544
                                    int src_stride,
545
                                    int dst_stride,
546
                                    const uint16_t* src_ptr,
547
42
                                    uint16_t* dst_ptr) {
548
42
  void (*ScaleRowUp)(const uint16_t* src_ptr, uint16_t* dst_ptr,
549
42
                     int dst_width) = ScaleRowUp2_Linear_16_Any_C;
550
42
  int i;
551
42
  int y;
552
42
  int dy;
553
554
42
  (void)src_width;
555
  // This function can only scale up by 2 times horizontally.
556
42
  assert(src_width == ((dst_width + 1) / 2));
557
558
42
  if (dst_height == 1) {
559
9
    ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr,
560
9
               dst_width);
561
33
  } else {
562
33
    dy = FixedDiv(src_height - 1, dst_height - 1);
563
33
    y = (1 << 15) - 1;
564
85.8k
    for (i = 0; i < dst_height; ++i) {
565
85.8k
      ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width);
566
85.8k
      dst_ptr += dst_stride;
567
85.8k
      y += dy;
568
85.8k
    }
569
33
  }
570
42
}
571
572
// Scale at most 12 bit plane, up by 2 times.
573
// This is an optimized version for scaling up a plane to 2 times of
574
// its original size, using bilinear interpolation.
575
// stride is in count of uint16_t.
576
// This is used to scale U and V planes of I010 to I410 and I012 to I412.
577
static void ScalePlaneUp2_12_Bilinear(int src_width,
578
                                      int src_height,
579
                                      int dst_width,
580
                                      int dst_height,
581
                                      int src_stride,
582
                                      int dst_stride,
583
                                      const uint16_t* src_ptr,
584
26
                                      uint16_t* dst_ptr) {
585
26
  void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride,
586
26
                      uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) =
587
26
      ScaleRowUp2_Bilinear_16_Any_C;
588
26
  int x;
589
590
26
  (void)src_width;
591
  // This function can only scale up by 2 times.
592
26
  assert(src_width == ((dst_width + 1) / 2));
593
26
  assert(src_height == ((dst_height + 1) / 2));
594
595
26
  Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
596
26
  dst_ptr += dst_stride;
597
79
  for (x = 0; x < src_height - 1; ++x) {
598
53
    Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width);
599
53
    src_ptr += src_stride;
600
53
    dst_ptr += 2 * dst_stride;
601
53
  }
602
26
  if (!(dst_height & 1)) {
603
19
    Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
604
19
  }
605
26
}
606
607
static void ScalePlaneUp2_16_Linear(int src_width,
608
                                    int src_height,
609
                                    int dst_width,
610
                                    int dst_height,
611
                                    int src_stride,
612
                                    int dst_stride,
613
                                    const uint16_t* src_ptr,
614
0
                                    uint16_t* dst_ptr) {
615
0
  void (*ScaleRowUp)(const uint16_t* src_ptr, uint16_t* dst_ptr,
616
0
                     int dst_width) = ScaleRowUp2_Linear_16_Any_C;
617
0
  int i;
618
0
  int y;
619
0
  int dy;
620
621
0
  (void)src_width;
622
  // This function can only scale up by 2 times horizontally.
623
0
  assert(src_width == ((dst_width + 1) / 2));
624
625
0
  if (dst_height == 1) {
626
0
    ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr,
627
0
               dst_width);
628
0
  } else {
629
0
    dy = FixedDiv(src_height - 1, dst_height - 1);
630
0
    y = (1 << 15) - 1;
631
0
    for (i = 0; i < dst_height; ++i) {
632
0
      ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width);
633
0
      dst_ptr += dst_stride;
634
0
      y += dy;
635
0
    }
636
0
  }
637
0
}
638
639
static void ScalePlaneUp2_16_Bilinear(int src_width,
640
                                      int src_height,
641
                                      int dst_width,
642
                                      int dst_height,
643
                                      int src_stride,
644
                                      int dst_stride,
645
                                      const uint16_t* src_ptr,
646
0
                                      uint16_t* dst_ptr) {
647
0
  void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride,
648
0
                      uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) =
649
0
      ScaleRowUp2_Bilinear_16_Any_C;
650
0
  int x;
651
652
0
  (void)src_width;
653
  // This function can only scale up by 2 times.
654
0
  assert(src_width == ((dst_width + 1) / 2));
655
0
  assert(src_height == ((dst_height + 1) / 2));
656
657
0
  Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
658
0
  dst_ptr += dst_stride;
659
0
  for (x = 0; x < src_height - 1; ++x) {
660
0
    Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width);
661
0
    src_ptr += src_stride;
662
0
    dst_ptr += 2 * dst_stride;
663
0
  }
664
0
  if (!(dst_height & 1)) {
665
0
    Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
666
0
  }
667
0
}
668
669
static int ScalePlaneBilinearUp_16(int src_width,
670
                                   int src_height,
671
                                   int dst_width,
672
                                   int dst_height,
673
                                   int src_stride,
674
                                   int dst_stride,
675
                                   const uint16_t* src_ptr,
676
                                   uint16_t* dst_ptr,
677
1.24k
                                   enum FilterMode filtering) {
678
1.24k
  int j;
679
  // Initial source x/y coordinate and step values as 16.16 fixed point.
680
1.24k
  int x = 0;
681
1.24k
  int y = 0;
682
1.24k
  int dx = 0;
683
1.24k
  int dy = 0;
684
1.24k
  const int max_y = (src_height - 1) << 16;
685
1.24k
  void (*InterpolateRow)(uint16_t* dst_ptr, const uint16_t* src_ptr,
686
1.24k
                         ptrdiff_t src_stride, int dst_width,
687
1.24k
                         int source_y_fraction) = InterpolateRow_16_C;
688
1.24k
  void (*ScaleFilterCols)(uint16_t* dst_ptr, const uint16_t* src_ptr,
689
1.24k
                          int dst_width, int x, int dx) =
690
1.24k
      filtering ? ScaleFilterCols_16_C : ScaleCols_16_C;
691
1.24k
  ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
692
1.24k
             &dx, &dy);
693
1.24k
  src_width = Abs(src_width);
694
695
1.24k
  if (filtering && src_width >= 32768) {
696
0
    ScaleFilterCols = ScaleFilterCols64_16_C;
697
0
  }
698
1.24k
  if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
699
0
    ScaleFilterCols = ScaleColsUp2_16_C;
700
0
  }
701
1.24k
  if (y > max_y) {
702
193
    y = max_y;
703
193
  }
704
1.24k
  {
705
1.24k
    int yi = y >> 16;
706
1.24k
    const uint16_t* src = src_ptr + yi * (int64_t)src_stride;
707
708
    // Allocate 2 row buffers.
709
1.24k
    const int row_size = (dst_width + 31) & ~31;
710
1.24k
    align_buffer_64(row, row_size * 4);
711
1.24k
    int rowstride = row_size;
712
1.24k
    int lasty = yi;
713
1.24k
    uint16_t* rowptr = (uint16_t*)row;
714
1.24k
    if (!row)
715
0
      return 1;
716
717
1.24k
    ScaleFilterCols(rowptr, src, dst_width, x, dx);
718
1.24k
    if (src_height > 1) {
719
1.03k
      src += src_stride;
720
1.03k
    }
721
1.24k
    ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx);
722
1.24k
    if (src_height > 2) {
723
899
      src += src_stride;
724
899
    }
725
726
8.76M
    for (j = 0; j < dst_height; ++j) {
727
8.76M
      yi = y >> 16;
728
8.76M
      if (yi != lasty) {
729
146k
        if (y > max_y) {
730
0
          y = max_y;
731
0
          yi = y >> 16;
732
0
          src = src_ptr + yi * (int64_t)src_stride;
733
0
        }
734
146k
        if (yi != lasty) {
735
146k
          ScaleFilterCols(rowptr, src, dst_width, x, dx);
736
146k
          rowptr += rowstride;
737
146k
          rowstride = -rowstride;
738
146k
          lasty = yi;
739
146k
          if ((y + 65536) < max_y) {
740
145k
            src += src_stride;
741
145k
          }
742
146k
        }
743
146k
      }
744
8.76M
      if (filtering == kFilterLinear) {
745
1.47M
        InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0);
746
7.29M
      } else {
747
7.29M
        int yf = (y >> 8) & 255;
748
7.29M
        InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf);
749
7.29M
      }
750
8.76M
      dst_ptr += dst_stride;
751
8.76M
      y += dy;
752
8.76M
    }
753
1.24k
    free_aligned_buffer_64(row);
754
1.24k
  }
755
0
  return 0;
756
1.24k
}
757
758
// Scale Plane to/from any dimensions, without interpolation.
759
// Fixed point math is used for performance: The upper 16 bits
760
// of x and dx is the integer part of the source position and
761
// the lower 16 bits are the fixed decimal part.
762
763
static void ScalePlaneSimple(int src_width,
764
                             int src_height,
765
                             int dst_width,
766
                             int dst_height,
767
                             int src_stride,
768
                             int dst_stride,
769
                             const uint8_t* src_ptr,
770
511
                             uint8_t* dst_ptr) {
771
511
  int i;
772
511
  void (*ScaleCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, int dst_width,
773
511
                    int x, int dx) = ScaleCols_C;
774
  // Initial source x/y coordinate and step values as 16.16 fixed point.
775
511
  int x = 0;
776
511
  int y = 0;
777
511
  int dx = 0;
778
511
  int dy = 0;
779
511
  ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y,
780
511
             &dx, &dy);
781
511
  src_width = Abs(src_width);
782
783
511
  if (src_width * 2 == dst_width && x < 0x8000) {
784
87
    ScaleCols = ScaleColsUp2_C;
785
87
  }
786
787
914k
  for (i = 0; i < dst_height; ++i) {
788
913k
    ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t)src_stride, dst_width, x,
789
913k
              dx);
790
913k
    dst_ptr += dst_stride;
791
913k
    y += dy;
792
913k
  }
793
511
}
794
795
static void ScalePlaneSimple_16(int src_width,
796
                                int src_height,
797
                                int dst_width,
798
                                int dst_height,
799
                                int src_stride,
800
                                int dst_stride,
801
                                const uint16_t* src_ptr,
802
311
                                uint16_t* dst_ptr) {
803
311
  int i;
804
311
  void (*ScaleCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, int dst_width,
805
311
                    int x, int dx) = ScaleCols_16_C;
806
  // Initial source x/y coordinate and step values as 16.16 fixed point.
807
311
  int x = 0;
808
311
  int y = 0;
809
311
  int dx = 0;
810
311
  int dy = 0;
811
311
  ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y,
812
311
             &dx, &dy);
813
311
  src_width = Abs(src_width);
814
815
311
  if (src_width * 2 == dst_width && x < 0x8000) {
816
72
    ScaleCols = ScaleColsUp2_16_C;
817
72
  }
818
819
165k
  for (i = 0; i < dst_height; ++i) {
820
165k
    ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t)src_stride, dst_width, x,
821
165k
              dx);
822
165k
    dst_ptr += dst_stride;
823
165k
    y += dy;
824
165k
  }
825
311
}
826
827
// Scale a plane.
828
// This function dispatches to a specialized scaler based on scale factor.
829
int ScalePlane(const uint8_t* src,
830
               int src_stride,
831
               int src_width,
832
               int src_height,
833
               uint8_t* dst,
834
               int dst_stride,
835
               int dst_width,
836
               int dst_height,
837
2.33k
               enum FilterMode filtering) {
838
  // Simplify filtering when possible.
839
2.33k
  filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
840
2.33k
                                filtering);
841
842
  // Negative height means invert the image.
843
2.33k
  if (src_height < 0) {
844
0
    src_height = -src_height;
845
0
    src = src + (src_height - 1) * (int64_t)src_stride;
846
0
    src_stride = -src_stride;
847
0
  }
848
  // Use specialized scales to improve performance for common resolutions.
849
  // For example, all the 1/2 scalings will use ScalePlaneDown2()
850
2.33k
  if (dst_width == src_width && dst_height == src_height) {
851
    // Straight copy.
852
14
    CopyPlane(src, src_stride, dst, dst_stride, dst_width, dst_height);
853
14
    return 0;
854
14
  }
855
2.31k
  if (dst_width == src_width && filtering != kFilterBox) {
856
476
    int dy = 0;
857
476
    int y = 0;
858
    // When scaling down, use the center 2 rows to filter.
859
    // When scaling up, last row of destination uses the last 2 source rows.
860
476
    if (dst_height <= src_height) {
861
333
      dy = FixedDiv(src_height, dst_height);
862
333
      y = CENTERSTART(dy, -32768);  // Subtract 0.5 (32768) to center filter.
863
333
    } else if (src_height > 1 && dst_height > 1) {
864
58
      dy = FixedDiv1(src_height, dst_height);
865
58
    }
866
    // Arbitrary scale vertically, but unscaled horizontally.
867
476
    ScalePlaneVertical(src_height, dst_width, dst_height, src_stride,
868
476
                       dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering);
869
476
    return 0;
870
476
  }
871
1.84k
  if (filtering == kFilterBox && dst_height * 2 < src_height) {
872
272
    return ScalePlaneBox(src_width, src_height, dst_width, dst_height,
873
272
                         src_stride, dst_stride, src, dst);
874
272
  }
875
1.56k
  if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) {
876
54
    ScalePlaneUp2_Linear(src_width, src_height, dst_width, dst_height,
877
54
                         src_stride, dst_stride, src, dst);
878
54
    return 0;
879
54
  }
880
1.51k
  if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width &&
881
60
      (filtering == kFilterBilinear || filtering == kFilterBox)) {
882
34
    ScalePlaneUp2_Bilinear(src_width, src_height, dst_width, dst_height,
883
34
                           src_stride, dst_stride, src, dst);
884
34
    return 0;
885
34
  }
886
1.48k
  if (filtering && dst_height > src_height) {
887
569
    return ScalePlaneBilinearUp(src_width, src_height, dst_width, dst_height,
888
569
                                src_stride, dst_stride, src, dst, filtering);
889
569
  }
890
912
  if (filtering) {
891
401
    return ScalePlaneBilinearDown(src_width, src_height, dst_width, dst_height,
892
401
                                  src_stride, dst_stride, src, dst, filtering);
893
401
  }
894
511
  ScalePlaneSimple(src_width, src_height, dst_width, dst_height, src_stride,
895
511
                   dst_stride, src, dst);
896
511
  return 0;
897
912
}
898
899
int ScalePlane_16(const uint16_t* src,
900
                  int src_stride,
901
                  int src_width,
902
                  int src_height,
903
                  uint16_t* dst,
904
                  int dst_stride,
905
                  int dst_width,
906
                  int dst_height,
907
2.43k
                  enum FilterMode filtering) {
908
  // Simplify filtering when possible.
909
2.43k
  filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
910
2.43k
                                filtering);
911
912
  // Negative height means invert the image.
913
2.43k
  if (src_height < 0) {
914
0
    src_height = -src_height;
915
0
    src = src + (src_height - 1) * (int64_t)src_stride;
916
0
    src_stride = -src_stride;
917
0
  }
918
  // Use specialized scales to improve performance for common resolutions.
919
  // For example, all the 1/2 scalings will use ScalePlaneDown2()
920
2.43k
  if (dst_width == src_width && dst_height == src_height) {
921
    // Straight copy.
922
12
    CopyPlane_16(src, src_stride, dst, dst_stride, dst_width, dst_height);
923
12
    return 0;
924
12
  }
925
2.42k
  if (dst_width == src_width && filtering != kFilterBox) {
926
169
    int dy = 0;
927
169
    int y = 0;
928
    // When scaling down, use the center 2 rows to filter.
929
    // When scaling up, last row of destination uses the last 2 source rows.
930
169
    if (dst_height <= src_height) {
931
86
      dy = FixedDiv(src_height, dst_height);
932
86
      y = CENTERSTART(dy, -32768);  // Subtract 0.5 (32768) to center filter.
933
      // When scaling up, ensure the last row of destination uses the last
934
      // source. Avoid divide by zero for dst_height but will do no scaling
935
      // later.
936
86
    } else if (src_height > 1 && dst_height > 1) {
937
53
      dy = FixedDiv1(src_height, dst_height);
938
53
    }
939
    // Arbitrary scale vertically, but unscaled horizontally.
940
169
    ScalePlaneVertical_16(src_height, dst_width, dst_height, src_stride,
941
169
                          dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering);
942
169
    return 0;
943
169
  }
944
2.25k
  if (filtering == kFilterBox && dst_height * 2 < src_height) {
945
163
    return ScalePlaneBox_16(src_width, src_height, dst_width, dst_height,
946
163
                            src_stride, dst_stride, src, dst);
947
163
  }
948
2.08k
  if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) {
949
0
    ScalePlaneUp2_16_Linear(src_width, src_height, dst_width, dst_height,
950
0
                            src_stride, dst_stride, src, dst);
951
0
    return 0;
952
0
  }
953
2.08k
  if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width &&
954
14
      (filtering == kFilterBilinear || filtering == kFilterBox)) {
955
0
    ScalePlaneUp2_16_Bilinear(src_width, src_height, dst_width, dst_height,
956
0
                              src_stride, dst_stride, src, dst);
957
0
    return 0;
958
0
  }
959
2.08k
  if (filtering && dst_height > src_height) {
960
1.24k
    return ScalePlaneBilinearUp_16(src_width, src_height, dst_width, dst_height,
961
1.24k
                                   src_stride, dst_stride, src, dst, filtering);
962
1.24k
  }
963
847
  if (filtering) {
964
536
    return ScalePlaneBilinearDown_16(src_width, src_height, dst_width,
965
536
                                     dst_height, src_stride, dst_stride, src,
966
536
                                     dst, filtering);
967
536
  }
968
311
  ScalePlaneSimple_16(src_width, src_height, dst_width, dst_height, src_stride,
969
311
                      dst_stride, src, dst);
970
311
  return 0;
971
847
}
972
973
int ScalePlane_12(const uint16_t* src,
974
                  int src_stride,
975
                  int src_width,
976
                  int src_height,
977
                  uint16_t* dst,
978
                  int dst_stride,
979
                  int dst_width,
980
                  int dst_height,
981
2.50k
                  enum FilterMode filtering) {
982
  // Simplify filtering when possible.
983
2.50k
  filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
984
2.50k
                                filtering);
985
986
  // Negative height means invert the image.
987
2.50k
  if (src_height < 0) {
988
0
    src_height = -src_height;
989
0
    src = src + (src_height - 1) * (int64_t)src_stride;
990
0
    src_stride = -src_stride;
991
0
  }
992
993
2.50k
  if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) {
994
42
    ScalePlaneUp2_12_Linear(src_width, src_height, dst_width, dst_height,
995
42
                            src_stride, dst_stride, src, dst);
996
42
    return 0;
997
42
  }
998
2.45k
  if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width &&
999
50
      (filtering == kFilterBilinear || filtering == kFilterBox)) {
1000
26
    ScalePlaneUp2_12_Bilinear(src_width, src_height, dst_width, dst_height,
1001
26
                              src_stride, dst_stride, src, dst);
1002
26
    return 0;
1003
26
  }
1004
1005
2.43k
  return ScalePlane_16(src, src_stride, src_width, src_height, dst, dst_stride,
1006
2.43k
                       dst_width, dst_height, filtering);
1007
2.45k
}