1/*
2 *  Copyright 2013 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_row.h"
20
21#ifdef __cplusplus
22namespace libyuv {
23extern "C" {
24#endif
25
26static __inline int Abs(int v) {
27  return v >= 0 ? v : -v;
28}
29
30// CPU agnostic row functions
31void ScaleRowDown2_C(const uint8* src_ptr, ptrdiff_t src_stride,
32                     uint8* dst, int dst_width) {
33  int x;
34  for (x = 0; x < dst_width - 1; x += 2) {
35    dst[0] = src_ptr[1];
36    dst[1] = src_ptr[3];
37    dst += 2;
38    src_ptr += 4;
39  }
40  if (dst_width & 1) {
41    dst[0] = src_ptr[1];
42  }
43}
44
45void ScaleRowDown2_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
46                        uint16* dst, int dst_width) {
47  int x;
48  for (x = 0; x < dst_width - 1; x += 2) {
49    dst[0] = src_ptr[1];
50    dst[1] = src_ptr[3];
51    dst += 2;
52    src_ptr += 4;
53  }
54  if (dst_width & 1) {
55    dst[0] = src_ptr[1];
56  }
57}
58
59void ScaleRowDown2Linear_C(const uint8* src_ptr, ptrdiff_t src_stride,
60                           uint8* dst, int dst_width) {
61  const uint8* s = src_ptr;
62  int x;
63  for (x = 0; x < dst_width - 1; x += 2) {
64    dst[0] = (s[0] + s[1] + 1) >> 1;
65    dst[1] = (s[2] + s[3] + 1) >> 1;
66    dst += 2;
67    s += 4;
68  }
69  if (dst_width & 1) {
70    dst[0] = (s[0] + s[1] + 1) >> 1;
71  }
72}
73
74void ScaleRowDown2Linear_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
75                              uint16* dst, int dst_width) {
76  const uint16* s = src_ptr;
77  int x;
78  for (x = 0; x < dst_width - 1; x += 2) {
79    dst[0] = (s[0] + s[1] + 1) >> 1;
80    dst[1] = (s[2] + s[3] + 1) >> 1;
81    dst += 2;
82    s += 4;
83  }
84  if (dst_width & 1) {
85    dst[0] = (s[0] + s[1] + 1) >> 1;
86  }
87}
88
89void ScaleRowDown2Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
90                        uint8* dst, int dst_width) {
91  const uint8* s = src_ptr;
92  const uint8* t = src_ptr + src_stride;
93  int x;
94  for (x = 0; x < dst_width - 1; x += 2) {
95    dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
96    dst[1] = (s[2] + s[3] + t[2] + t[3] + 2) >> 2;
97    dst += 2;
98    s += 4;
99    t += 4;
100  }
101  if (dst_width & 1) {
102    dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
103  }
104}
105
106void ScaleRowDown2Box_Odd_C(const uint8* src_ptr, ptrdiff_t src_stride,
107                            uint8* dst, int dst_width) {
108  const uint8* s = src_ptr;
109  const uint8* t = src_ptr + src_stride;
110  int x;
111  dst_width -= 1;
112  for (x = 0; x < dst_width - 1; x += 2) {
113    dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
114    dst[1] = (s[2] + s[3] + t[2] + t[3] + 2) >> 2;
115    dst += 2;
116    s += 4;
117    t += 4;
118  }
119  if (dst_width & 1) {
120    dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
121    dst += 1;
122    s += 2;
123    t += 2;
124  }
125  dst[0] = (s[0] + t[0] + 1) >> 1;
126}
127
128void ScaleRowDown2Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
129                           uint16* dst, int dst_width) {
130  const uint16* s = src_ptr;
131  const uint16* t = src_ptr + src_stride;
132  int x;
133  for (x = 0; x < dst_width - 1; x += 2) {
134    dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
135    dst[1] = (s[2] + s[3] + t[2] + t[3] + 2) >> 2;
136    dst += 2;
137    s += 4;
138    t += 4;
139  }
140  if (dst_width & 1) {
141    dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
142  }
143}
144
145void ScaleRowDown4_C(const uint8* src_ptr, ptrdiff_t src_stride,
146                     uint8* dst, int dst_width) {
147  int x;
148  for (x = 0; x < dst_width - 1; x += 2) {
149    dst[0] = src_ptr[2];
150    dst[1] = src_ptr[6];
151    dst += 2;
152    src_ptr += 8;
153  }
154  if (dst_width & 1) {
155    dst[0] = src_ptr[2];
156  }
157}
158
159void ScaleRowDown4_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
160                        uint16* dst, int dst_width) {
161  int x;
162  for (x = 0; x < dst_width - 1; x += 2) {
163    dst[0] = src_ptr[2];
164    dst[1] = src_ptr[6];
165    dst += 2;
166    src_ptr += 8;
167  }
168  if (dst_width & 1) {
169    dst[0] = src_ptr[2];
170  }
171}
172
173void ScaleRowDown4Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
174                        uint8* dst, int dst_width) {
175  intptr_t stride = src_stride;
176  int x;
177  for (x = 0; x < dst_width - 1; x += 2) {
178    dst[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[3] +
179             src_ptr[stride + 0] + src_ptr[stride + 1] +
180             src_ptr[stride + 2] + src_ptr[stride + 3] +
181             src_ptr[stride * 2 + 0] + src_ptr[stride * 2 + 1] +
182             src_ptr[stride * 2 + 2] + src_ptr[stride * 2 + 3] +
183             src_ptr[stride * 3 + 0] + src_ptr[stride * 3 + 1] +
184             src_ptr[stride * 3 + 2] + src_ptr[stride * 3 + 3] +
185             8) >> 4;
186    dst[1] = (src_ptr[4] + src_ptr[5] + src_ptr[6] + src_ptr[7] +
187             src_ptr[stride + 4] + src_ptr[stride + 5] +
188             src_ptr[stride + 6] + src_ptr[stride + 7] +
189             src_ptr[stride * 2 + 4] + src_ptr[stride * 2 + 5] +
190             src_ptr[stride * 2 + 6] + src_ptr[stride * 2 + 7] +
191             src_ptr[stride * 3 + 4] + src_ptr[stride * 3 + 5] +
192             src_ptr[stride * 3 + 6] + src_ptr[stride * 3 + 7] +
193             8) >> 4;
194    dst += 2;
195    src_ptr += 8;
196  }
197  if (dst_width & 1) {
198    dst[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[3] +
199             src_ptr[stride + 0] + src_ptr[stride + 1] +
200             src_ptr[stride + 2] + src_ptr[stride + 3] +
201             src_ptr[stride * 2 + 0] + src_ptr[stride * 2 + 1] +
202             src_ptr[stride * 2 + 2] + src_ptr[stride * 2 + 3] +
203             src_ptr[stride * 3 + 0] + src_ptr[stride * 3 + 1] +
204             src_ptr[stride * 3 + 2] + src_ptr[stride * 3 + 3] +
205             8) >> 4;
206  }
207}
208
209void ScaleRowDown4Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
210                           uint16* dst, int dst_width) {
211  intptr_t stride = src_stride;
212  int x;
213  for (x = 0; x < dst_width - 1; x += 2) {
214    dst[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[3] +
215             src_ptr[stride + 0] + src_ptr[stride + 1] +
216             src_ptr[stride + 2] + src_ptr[stride + 3] +
217             src_ptr[stride * 2 + 0] + src_ptr[stride * 2 + 1] +
218             src_ptr[stride * 2 + 2] + src_ptr[stride * 2 + 3] +
219             src_ptr[stride * 3 + 0] + src_ptr[stride * 3 + 1] +
220             src_ptr[stride * 3 + 2] + src_ptr[stride * 3 + 3] +
221             8) >> 4;
222    dst[1] = (src_ptr[4] + src_ptr[5] + src_ptr[6] + src_ptr[7] +
223             src_ptr[stride + 4] + src_ptr[stride + 5] +
224             src_ptr[stride + 6] + src_ptr[stride + 7] +
225             src_ptr[stride * 2 + 4] + src_ptr[stride * 2 + 5] +
226             src_ptr[stride * 2 + 6] + src_ptr[stride * 2 + 7] +
227             src_ptr[stride * 3 + 4] + src_ptr[stride * 3 + 5] +
228             src_ptr[stride * 3 + 6] + src_ptr[stride * 3 + 7] +
229             8) >> 4;
230    dst += 2;
231    src_ptr += 8;
232  }
233  if (dst_width & 1) {
234    dst[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[3] +
235             src_ptr[stride + 0] + src_ptr[stride + 1] +
236             src_ptr[stride + 2] + src_ptr[stride + 3] +
237             src_ptr[stride * 2 + 0] + src_ptr[stride * 2 + 1] +
238             src_ptr[stride * 2 + 2] + src_ptr[stride * 2 + 3] +
239             src_ptr[stride * 3 + 0] + src_ptr[stride * 3 + 1] +
240             src_ptr[stride * 3 + 2] + src_ptr[stride * 3 + 3] +
241             8) >> 4;
242  }
243}
244
245void ScaleRowDown34_C(const uint8* src_ptr, ptrdiff_t src_stride,
246                      uint8* dst, int dst_width) {
247  int x;
248  assert((dst_width % 3 == 0) && (dst_width > 0));
249  for (x = 0; x < dst_width; x += 3) {
250    dst[0] = src_ptr[0];
251    dst[1] = src_ptr[1];
252    dst[2] = src_ptr[3];
253    dst += 3;
254    src_ptr += 4;
255  }
256}
257
258void ScaleRowDown34_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
259                         uint16* dst, int dst_width) {
260  int x;
261  assert((dst_width % 3 == 0) && (dst_width > 0));
262  for (x = 0; x < dst_width; x += 3) {
263    dst[0] = src_ptr[0];
264    dst[1] = src_ptr[1];
265    dst[2] = src_ptr[3];
266    dst += 3;
267    src_ptr += 4;
268  }
269}
270
271// Filter rows 0 and 1 together, 3 : 1
272void ScaleRowDown34_0_Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
273                            uint8* d, int dst_width) {
274  const uint8* s = src_ptr;
275  const uint8* t = src_ptr + src_stride;
276  int x;
277  assert((dst_width % 3 == 0) && (dst_width > 0));
278  for (x = 0; x < dst_width; x += 3) {
279    uint8 a0 = (s[0] * 3 + s[1] * 1 + 2) >> 2;
280    uint8 a1 = (s[1] * 1 + s[2] * 1 + 1) >> 1;
281    uint8 a2 = (s[2] * 1 + s[3] * 3 + 2) >> 2;
282    uint8 b0 = (t[0] * 3 + t[1] * 1 + 2) >> 2;
283    uint8 b1 = (t[1] * 1 + t[2] * 1 + 1) >> 1;
284    uint8 b2 = (t[2] * 1 + t[3] * 3 + 2) >> 2;
285    d[0] = (a0 * 3 + b0 + 2) >> 2;
286    d[1] = (a1 * 3 + b1 + 2) >> 2;
287    d[2] = (a2 * 3 + b2 + 2) >> 2;
288    d += 3;
289    s += 4;
290    t += 4;
291  }
292}
293
294void ScaleRowDown34_0_Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
295                               uint16* d, int dst_width) {
296  const uint16* s = src_ptr;
297  const uint16* t = src_ptr + src_stride;
298  int x;
299  assert((dst_width % 3 == 0) && (dst_width > 0));
300  for (x = 0; x < dst_width; x += 3) {
301    uint16 a0 = (s[0] * 3 + s[1] * 1 + 2) >> 2;
302    uint16 a1 = (s[1] * 1 + s[2] * 1 + 1) >> 1;
303    uint16 a2 = (s[2] * 1 + s[3] * 3 + 2) >> 2;
304    uint16 b0 = (t[0] * 3 + t[1] * 1 + 2) >> 2;
305    uint16 b1 = (t[1] * 1 + t[2] * 1 + 1) >> 1;
306    uint16 b2 = (t[2] * 1 + t[3] * 3 + 2) >> 2;
307    d[0] = (a0 * 3 + b0 + 2) >> 2;
308    d[1] = (a1 * 3 + b1 + 2) >> 2;
309    d[2] = (a2 * 3 + b2 + 2) >> 2;
310    d += 3;
311    s += 4;
312    t += 4;
313  }
314}
315
316// Filter rows 1 and 2 together, 1 : 1
317void ScaleRowDown34_1_Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
318                            uint8* d, int dst_width) {
319  const uint8* s = src_ptr;
320  const uint8* t = src_ptr + src_stride;
321  int x;
322  assert((dst_width % 3 == 0) && (dst_width > 0));
323  for (x = 0; x < dst_width; x += 3) {
324    uint8 a0 = (s[0] * 3 + s[1] * 1 + 2) >> 2;
325    uint8 a1 = (s[1] * 1 + s[2] * 1 + 1) >> 1;
326    uint8 a2 = (s[2] * 1 + s[3] * 3 + 2) >> 2;
327    uint8 b0 = (t[0] * 3 + t[1] * 1 + 2) >> 2;
328    uint8 b1 = (t[1] * 1 + t[2] * 1 + 1) >> 1;
329    uint8 b2 = (t[2] * 1 + t[3] * 3 + 2) >> 2;
330    d[0] = (a0 + b0 + 1) >> 1;
331    d[1] = (a1 + b1 + 1) >> 1;
332    d[2] = (a2 + b2 + 1) >> 1;
333    d += 3;
334    s += 4;
335    t += 4;
336  }
337}
338
339void ScaleRowDown34_1_Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
340                               uint16* d, int dst_width) {
341  const uint16* s = src_ptr;
342  const uint16* t = src_ptr + src_stride;
343  int x;
344  assert((dst_width % 3 == 0) && (dst_width > 0));
345  for (x = 0; x < dst_width; x += 3) {
346    uint16 a0 = (s[0] * 3 + s[1] * 1 + 2) >> 2;
347    uint16 a1 = (s[1] * 1 + s[2] * 1 + 1) >> 1;
348    uint16 a2 = (s[2] * 1 + s[3] * 3 + 2) >> 2;
349    uint16 b0 = (t[0] * 3 + t[1] * 1 + 2) >> 2;
350    uint16 b1 = (t[1] * 1 + t[2] * 1 + 1) >> 1;
351    uint16 b2 = (t[2] * 1 + t[3] * 3 + 2) >> 2;
352    d[0] = (a0 + b0 + 1) >> 1;
353    d[1] = (a1 + b1 + 1) >> 1;
354    d[2] = (a2 + b2 + 1) >> 1;
355    d += 3;
356    s += 4;
357    t += 4;
358  }
359}
360
361// Scales a single row of pixels using point sampling.
362void ScaleCols_C(uint8* dst_ptr, const uint8* src_ptr,
363                 int dst_width, int x, int dx) {
364  int j;
365  for (j = 0; j < dst_width - 1; j += 2) {
366    dst_ptr[0] = src_ptr[x >> 16];
367    x += dx;
368    dst_ptr[1] = src_ptr[x >> 16];
369    x += dx;
370    dst_ptr += 2;
371  }
372  if (dst_width & 1) {
373    dst_ptr[0] = src_ptr[x >> 16];
374  }
375}
376
377void ScaleCols_16_C(uint16* dst_ptr, const uint16* src_ptr,
378                    int dst_width, int x, int dx) {
379  int j;
380  for (j = 0; j < dst_width - 1; j += 2) {
381    dst_ptr[0] = src_ptr[x >> 16];
382    x += dx;
383    dst_ptr[1] = src_ptr[x >> 16];
384    x += dx;
385    dst_ptr += 2;
386  }
387  if (dst_width & 1) {
388    dst_ptr[0] = src_ptr[x >> 16];
389  }
390}
391
392// Scales a single row of pixels up by 2x using point sampling.
393void ScaleColsUp2_C(uint8* dst_ptr, const uint8* src_ptr,
394                    int dst_width, int x, int dx) {
395  int j;
396  for (j = 0; j < dst_width - 1; j += 2) {
397    dst_ptr[1] = dst_ptr[0] = src_ptr[0];
398    src_ptr += 1;
399    dst_ptr += 2;
400  }
401  if (dst_width & 1) {
402    dst_ptr[0] = src_ptr[0];
403  }
404}
405
406void ScaleColsUp2_16_C(uint16* dst_ptr, const uint16* src_ptr,
407                       int dst_width, int x, int dx) {
408  int j;
409  for (j = 0; j < dst_width - 1; j += 2) {
410    dst_ptr[1] = dst_ptr[0] = src_ptr[0];
411    src_ptr += 1;
412    dst_ptr += 2;
413  }
414  if (dst_width & 1) {
415    dst_ptr[0] = src_ptr[0];
416  }
417}
418
419// (1-f)a + fb can be replaced with a + f(b-a)
420#if defined(__arm__) || defined(__aarch64__)
421#define BLENDER(a, b, f) (uint8)((int)(a) + \
422    ((((int)((f)) * ((int)(b) - (int)(a))) + 0x8000) >> 16))
423#else
424// inteluses 7 bit math with rounding.
425#define BLENDER(a, b, f) (uint8)((int)(a) + \
426    (((int)((f) >> 9) * ((int)(b) - (int)(a)) + 0x40) >> 7))
427#endif
428
429void ScaleFilterCols_C(uint8* dst_ptr, const uint8* src_ptr,
430                       int dst_width, int x, int dx) {
431  int j;
432  for (j = 0; j < dst_width - 1; j += 2) {
433    int xi = x >> 16;
434    int a = src_ptr[xi];
435    int b = src_ptr[xi + 1];
436    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
437    x += dx;
438    xi = x >> 16;
439    a = src_ptr[xi];
440    b = src_ptr[xi + 1];
441    dst_ptr[1] = BLENDER(a, b, x & 0xffff);
442    x += dx;
443    dst_ptr += 2;
444  }
445  if (dst_width & 1) {
446    int xi = x >> 16;
447    int a = src_ptr[xi];
448    int b = src_ptr[xi + 1];
449    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
450  }
451}
452
453void ScaleFilterCols64_C(uint8* dst_ptr, const uint8* src_ptr,
454                         int dst_width, int x32, int dx) {
455  int64 x = (int64)(x32);
456  int j;
457  for (j = 0; j < dst_width - 1; j += 2) {
458    int64 xi = x >> 16;
459    int a = src_ptr[xi];
460    int b = src_ptr[xi + 1];
461    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
462    x += dx;
463    xi = x >> 16;
464    a = src_ptr[xi];
465    b = src_ptr[xi + 1];
466    dst_ptr[1] = BLENDER(a, b, x & 0xffff);
467    x += dx;
468    dst_ptr += 2;
469  }
470  if (dst_width & 1) {
471    int64 xi = x >> 16;
472    int a = src_ptr[xi];
473    int b = src_ptr[xi + 1];
474    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
475  }
476}
477#undef BLENDER
478
479// Same as 8 bit arm blender but return is cast to uint16
480#define BLENDER(a, b, f) (uint16)((int)(a) + \
481    ((((int)((f)) * ((int)(b) - (int)(a))) + 0x8000) >> 16))
482
483void ScaleFilterCols_16_C(uint16* dst_ptr, const uint16* src_ptr,
484                       int dst_width, int x, int dx) {
485  int j;
486  for (j = 0; j < dst_width - 1; j += 2) {
487    int xi = x >> 16;
488    int a = src_ptr[xi];
489    int b = src_ptr[xi + 1];
490    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
491    x += dx;
492    xi = x >> 16;
493    a = src_ptr[xi];
494    b = src_ptr[xi + 1];
495    dst_ptr[1] = BLENDER(a, b, x & 0xffff);
496    x += dx;
497    dst_ptr += 2;
498  }
499  if (dst_width & 1) {
500    int xi = x >> 16;
501    int a = src_ptr[xi];
502    int b = src_ptr[xi + 1];
503    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
504  }
505}
506
507void ScaleFilterCols64_16_C(uint16* dst_ptr, const uint16* src_ptr,
508                         int dst_width, int x32, int dx) {
509  int64 x = (int64)(x32);
510  int j;
511  for (j = 0; j < dst_width - 1; j += 2) {
512    int64 xi = x >> 16;
513    int a = src_ptr[xi];
514    int b = src_ptr[xi + 1];
515    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
516    x += dx;
517    xi = x >> 16;
518    a = src_ptr[xi];
519    b = src_ptr[xi + 1];
520    dst_ptr[1] = BLENDER(a, b, x & 0xffff);
521    x += dx;
522    dst_ptr += 2;
523  }
524  if (dst_width & 1) {
525    int64 xi = x >> 16;
526    int a = src_ptr[xi];
527    int b = src_ptr[xi + 1];
528    dst_ptr[0] = BLENDER(a, b, x & 0xffff);
529  }
530}
531#undef BLENDER
532
533void ScaleRowDown38_C(const uint8* src_ptr, ptrdiff_t src_stride,
534                      uint8* dst, int dst_width) {
535  int x;
536  assert(dst_width % 3 == 0);
537  for (x = 0; x < dst_width; x += 3) {
538    dst[0] = src_ptr[0];
539    dst[1] = src_ptr[3];
540    dst[2] = src_ptr[6];
541    dst += 3;
542    src_ptr += 8;
543  }
544}
545
546void ScaleRowDown38_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
547                         uint16* dst, int dst_width) {
548  int x;
549  assert(dst_width % 3 == 0);
550  for (x = 0; x < dst_width; x += 3) {
551    dst[0] = src_ptr[0];
552    dst[1] = src_ptr[3];
553    dst[2] = src_ptr[6];
554    dst += 3;
555    src_ptr += 8;
556  }
557}
558
559// 8x3 -> 3x1
560void ScaleRowDown38_3_Box_C(const uint8* src_ptr,
561                            ptrdiff_t src_stride,
562                            uint8* dst_ptr, int dst_width) {
563  intptr_t stride = src_stride;
564  int i;
565  assert((dst_width % 3 == 0) && (dst_width > 0));
566  for (i = 0; i < dst_width; i += 3) {
567    dst_ptr[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] +
568        src_ptr[stride + 0] + src_ptr[stride + 1] +
569        src_ptr[stride + 2] + src_ptr[stride * 2 + 0] +
570        src_ptr[stride * 2 + 1] + src_ptr[stride * 2 + 2]) *
571        (65536 / 9) >> 16;
572    dst_ptr[1] = (src_ptr[3] + src_ptr[4] + src_ptr[5] +
573        src_ptr[stride + 3] + src_ptr[stride + 4] +
574        src_ptr[stride + 5] + src_ptr[stride * 2 + 3] +
575        src_ptr[stride * 2 + 4] + src_ptr[stride * 2 + 5]) *
576        (65536 / 9) >> 16;
577    dst_ptr[2] = (src_ptr[6] + src_ptr[7] +
578        src_ptr[stride + 6] + src_ptr[stride + 7] +
579        src_ptr[stride * 2 + 6] + src_ptr[stride * 2 + 7]) *
580        (65536 / 6) >> 16;
581    src_ptr += 8;
582    dst_ptr += 3;
583  }
584}
585
586void ScaleRowDown38_3_Box_16_C(const uint16* src_ptr,
587                               ptrdiff_t src_stride,
588                               uint16* dst_ptr, int dst_width) {
589  intptr_t stride = src_stride;
590  int i;
591  assert((dst_width % 3 == 0) && (dst_width > 0));
592  for (i = 0; i < dst_width; i += 3) {
593    dst_ptr[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] +
594        src_ptr[stride + 0] + src_ptr[stride + 1] +
595        src_ptr[stride + 2] + src_ptr[stride * 2 + 0] +
596        src_ptr[stride * 2 + 1] + src_ptr[stride * 2 + 2]) *
597        (65536 / 9) >> 16;
598    dst_ptr[1] = (src_ptr[3] + src_ptr[4] + src_ptr[5] +
599        src_ptr[stride + 3] + src_ptr[stride + 4] +
600        src_ptr[stride + 5] + src_ptr[stride * 2 + 3] +
601        src_ptr[stride * 2 + 4] + src_ptr[stride * 2 + 5]) *
602        (65536 / 9) >> 16;
603    dst_ptr[2] = (src_ptr[6] + src_ptr[7] +
604        src_ptr[stride + 6] + src_ptr[stride + 7] +
605        src_ptr[stride * 2 + 6] + src_ptr[stride * 2 + 7]) *
606        (65536 / 6) >> 16;
607    src_ptr += 8;
608    dst_ptr += 3;
609  }
610}
611
612// 8x2 -> 3x1
613void ScaleRowDown38_2_Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
614                            uint8* dst_ptr, int dst_width) {
615  intptr_t stride = src_stride;
616  int i;
617  assert((dst_width % 3 == 0) && (dst_width > 0));
618  for (i = 0; i < dst_width; i += 3) {
619    dst_ptr[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] +
620        src_ptr[stride + 0] + src_ptr[stride + 1] +
621        src_ptr[stride + 2]) * (65536 / 6) >> 16;
622    dst_ptr[1] = (src_ptr[3] + src_ptr[4] + src_ptr[5] +
623        src_ptr[stride + 3] + src_ptr[stride + 4] +
624        src_ptr[stride + 5]) * (65536 / 6) >> 16;
625    dst_ptr[2] = (src_ptr[6] + src_ptr[7] +
626        src_ptr[stride + 6] + src_ptr[stride + 7]) *
627        (65536 / 4) >> 16;
628    src_ptr += 8;
629    dst_ptr += 3;
630  }
631}
632
633void ScaleRowDown38_2_Box_16_C(const uint16* src_ptr, ptrdiff_t src_stride,
634                               uint16* dst_ptr, int dst_width) {
635  intptr_t stride = src_stride;
636  int i;
637  assert((dst_width % 3 == 0) && (dst_width > 0));
638  for (i = 0; i < dst_width; i += 3) {
639    dst_ptr[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] +
640        src_ptr[stride + 0] + src_ptr[stride + 1] +
641        src_ptr[stride + 2]) * (65536 / 6) >> 16;
642    dst_ptr[1] = (src_ptr[3] + src_ptr[4] + src_ptr[5] +
643        src_ptr[stride + 3] + src_ptr[stride + 4] +
644        src_ptr[stride + 5]) * (65536 / 6) >> 16;
645    dst_ptr[2] = (src_ptr[6] + src_ptr[7] +
646        src_ptr[stride + 6] + src_ptr[stride + 7]) *
647        (65536 / 4) >> 16;
648    src_ptr += 8;
649    dst_ptr += 3;
650  }
651}
652
653void ScaleAddRow_C(const uint8* src_ptr, uint16* dst_ptr, int src_width) {
654  int x;
655  assert(src_width > 0);
656  for (x = 0; x < src_width - 1; x += 2) {
657    dst_ptr[0] += src_ptr[0];
658    dst_ptr[1] += src_ptr[1];
659    src_ptr += 2;
660    dst_ptr += 2;
661  }
662  if (src_width & 1) {
663    dst_ptr[0] += src_ptr[0];
664  }
665}
666
667void ScaleAddRow_16_C(const uint16* src_ptr, uint32* dst_ptr, int src_width) {
668  int x;
669  assert(src_width > 0);
670  for (x = 0; x < src_width - 1; x += 2) {
671    dst_ptr[0] += src_ptr[0];
672    dst_ptr[1] += src_ptr[1];
673    src_ptr += 2;
674    dst_ptr += 2;
675  }
676  if (src_width & 1) {
677    dst_ptr[0] += src_ptr[0];
678  }
679}
680
681void ScaleARGBRowDown2_C(const uint8* src_argb,
682                         ptrdiff_t src_stride,
683                         uint8* dst_argb, int dst_width) {
684  const uint32* src = (const uint32*)(src_argb);
685  uint32* dst = (uint32*)(dst_argb);
686
687  int x;
688  for (x = 0; x < dst_width - 1; x += 2) {
689    dst[0] = src[1];
690    dst[1] = src[3];
691    src += 4;
692    dst += 2;
693  }
694  if (dst_width & 1) {
695    dst[0] = src[1];
696  }
697}
698
699void ScaleARGBRowDown2Linear_C(const uint8* src_argb,
700                               ptrdiff_t src_stride,
701                               uint8* dst_argb, int dst_width) {
702  int x;
703  for (x = 0; x < dst_width; ++x) {
704    dst_argb[0] = (src_argb[0] + src_argb[4] + 1) >> 1;
705    dst_argb[1] = (src_argb[1] + src_argb[5] + 1) >> 1;
706    dst_argb[2] = (src_argb[2] + src_argb[6] + 1) >> 1;
707    dst_argb[3] = (src_argb[3] + src_argb[7] + 1) >> 1;
708    src_argb += 8;
709    dst_argb += 4;
710  }
711}
712
713void ScaleARGBRowDown2Box_C(const uint8* src_argb, ptrdiff_t src_stride,
714                            uint8* dst_argb, int dst_width) {
715  int x;
716  for (x = 0; x < dst_width; ++x) {
717    dst_argb[0] = (src_argb[0] + src_argb[4] +
718                  src_argb[src_stride] + src_argb[src_stride + 4] + 2) >> 2;
719    dst_argb[1] = (src_argb[1] + src_argb[5] +
720                  src_argb[src_stride + 1] + src_argb[src_stride + 5] + 2) >> 2;
721    dst_argb[2] = (src_argb[2] + src_argb[6] +
722                  src_argb[src_stride + 2] + src_argb[src_stride + 6] + 2) >> 2;
723    dst_argb[3] = (src_argb[3] + src_argb[7] +
724                  src_argb[src_stride + 3] + src_argb[src_stride + 7] + 2) >> 2;
725    src_argb += 8;
726    dst_argb += 4;
727  }
728}
729
730void ScaleARGBRowDownEven_C(const uint8* src_argb, ptrdiff_t src_stride,
731                            int src_stepx,
732                            uint8* dst_argb, int dst_width) {
733  const uint32* src = (const uint32*)(src_argb);
734  uint32* dst = (uint32*)(dst_argb);
735
736  int x;
737  for (x = 0; x < dst_width - 1; x += 2) {
738    dst[0] = src[0];
739    dst[1] = src[src_stepx];
740    src += src_stepx * 2;
741    dst += 2;
742  }
743  if (dst_width & 1) {
744    dst[0] = src[0];
745  }
746}
747
748void ScaleARGBRowDownEvenBox_C(const uint8* src_argb,
749                               ptrdiff_t src_stride,
750                               int src_stepx,
751                               uint8* dst_argb, int dst_width) {
752  int x;
753  for (x = 0; x < dst_width; ++x) {
754    dst_argb[0] = (src_argb[0] + src_argb[4] +
755                  src_argb[src_stride] + src_argb[src_stride + 4] + 2) >> 2;
756    dst_argb[1] = (src_argb[1] + src_argb[5] +
757                  src_argb[src_stride + 1] + src_argb[src_stride + 5] + 2) >> 2;
758    dst_argb[2] = (src_argb[2] + src_argb[6] +
759                  src_argb[src_stride + 2] + src_argb[src_stride + 6] + 2) >> 2;
760    dst_argb[3] = (src_argb[3] + src_argb[7] +
761                  src_argb[src_stride + 3] + src_argb[src_stride + 7] + 2) >> 2;
762    src_argb += src_stepx * 4;
763    dst_argb += 4;
764  }
765}
766
767// Scales a single row of pixels using point sampling.
768void ScaleARGBCols_C(uint8* dst_argb, const uint8* src_argb,
769                     int dst_width, int x, int dx) {
770  const uint32* src = (const uint32*)(src_argb);
771  uint32* dst = (uint32*)(dst_argb);
772  int j;
773  for (j = 0; j < dst_width - 1; j += 2) {
774    dst[0] = src[x >> 16];
775    x += dx;
776    dst[1] = src[x >> 16];
777    x += dx;
778    dst += 2;
779  }
780  if (dst_width & 1) {
781    dst[0] = src[x >> 16];
782  }
783}
784
785void ScaleARGBCols64_C(uint8* dst_argb, const uint8* src_argb,
786                       int dst_width, int x32, int dx) {
787  int64 x = (int64)(x32);
788  const uint32* src = (const uint32*)(src_argb);
789  uint32* dst = (uint32*)(dst_argb);
790  int j;
791  for (j = 0; j < dst_width - 1; j += 2) {
792    dst[0] = src[x >> 16];
793    x += dx;
794    dst[1] = src[x >> 16];
795    x += dx;
796    dst += 2;
797  }
798  if (dst_width & 1) {
799    dst[0] = src[x >> 16];
800  }
801}
802
803// Scales a single row of pixels up by 2x using point sampling.
804void ScaleARGBColsUp2_C(uint8* dst_argb, const uint8* src_argb,
805                        int dst_width, int x, int dx) {
806  const uint32* src = (const uint32*)(src_argb);
807  uint32* dst = (uint32*)(dst_argb);
808  int j;
809  for (j = 0; j < dst_width - 1; j += 2) {
810    dst[1] = dst[0] = src[0];
811    src += 1;
812    dst += 2;
813  }
814  if (dst_width & 1) {
815    dst[0] = src[0];
816  }
817}
818
819// TODO(fbarchard): Replace 0x7f ^ f with 128-f.  bug=607.
820// Mimics SSSE3 blender
821#define BLENDER1(a, b, f) ((a) * (0x7f ^ f) + (b) * f) >> 7
822#define BLENDERC(a, b, f, s) (uint32)( \
823    BLENDER1(((a) >> s) & 255, ((b) >> s) & 255, f) << s)
824#define BLENDER(a, b, f) \
825    BLENDERC(a, b, f, 24) | BLENDERC(a, b, f, 16) | \
826    BLENDERC(a, b, f, 8) | BLENDERC(a, b, f, 0)
827
828void ScaleARGBFilterCols_C(uint8* dst_argb, const uint8* src_argb,
829                           int dst_width, int x, int dx) {
830  const uint32* src = (const uint32*)(src_argb);
831  uint32* dst = (uint32*)(dst_argb);
832  int j;
833  for (j = 0; j < dst_width - 1; j += 2) {
834    int xi = x >> 16;
835    int xf = (x >> 9) & 0x7f;
836    uint32 a = src[xi];
837    uint32 b = src[xi + 1];
838    dst[0] = BLENDER(a, b, xf);
839    x += dx;
840    xi = x >> 16;
841    xf = (x >> 9) & 0x7f;
842    a = src[xi];
843    b = src[xi + 1];
844    dst[1] = BLENDER(a, b, xf);
845    x += dx;
846    dst += 2;
847  }
848  if (dst_width & 1) {
849    int xi = x >> 16;
850    int xf = (x >> 9) & 0x7f;
851    uint32 a = src[xi];
852    uint32 b = src[xi + 1];
853    dst[0] = BLENDER(a, b, xf);
854  }
855}
856
857void ScaleARGBFilterCols64_C(uint8* dst_argb, const uint8* src_argb,
858                             int dst_width, int x32, int dx) {
859  int64 x = (int64)(x32);
860  const uint32* src = (const uint32*)(src_argb);
861  uint32* dst = (uint32*)(dst_argb);
862  int j;
863  for (j = 0; j < dst_width - 1; j += 2) {
864    int64 xi = x >> 16;
865    int xf = (x >> 9) & 0x7f;
866    uint32 a = src[xi];
867    uint32 b = src[xi + 1];
868    dst[0] = BLENDER(a, b, xf);
869    x += dx;
870    xi = x >> 16;
871    xf = (x >> 9) & 0x7f;
872    a = src[xi];
873    b = src[xi + 1];
874    dst[1] = BLENDER(a, b, xf);
875    x += dx;
876    dst += 2;
877  }
878  if (dst_width & 1) {
879    int64 xi = x >> 16;
880    int xf = (x >> 9) & 0x7f;
881    uint32 a = src[xi];
882    uint32 b = src[xi + 1];
883    dst[0] = BLENDER(a, b, xf);
884  }
885}
886#undef BLENDER1
887#undef BLENDERC
888#undef BLENDER
889
890// Scale plane vertically with bilinear interpolation.
891void ScalePlaneVertical(int src_height,
892                        int dst_width, int dst_height,
893                        int src_stride, int dst_stride,
894                        const uint8* src_argb, uint8* dst_argb,
895                        int x, int y, int dy,
896                        int bpp, enum FilterMode filtering) {
897  // TODO(fbarchard): Allow higher bpp.
898  int dst_width_bytes = dst_width * bpp;
899  void (*InterpolateRow)(uint8* dst_argb, const uint8* src_argb,
900      ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
901      InterpolateRow_C;
902  const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
903  int j;
904  assert(bpp >= 1 && bpp <= 4);
905  assert(src_height != 0);
906  assert(dst_width > 0);
907  assert(dst_height > 0);
908  src_argb += (x >> 16) * bpp;
909#if defined(HAS_INTERPOLATEROW_SSSE3)
910  if (TestCpuFlag(kCpuHasSSSE3)) {
911    InterpolateRow = InterpolateRow_Any_SSSE3;
912    if (IS_ALIGNED(dst_width_bytes, 16)) {
913      InterpolateRow = InterpolateRow_SSSE3;
914    }
915  }
916#endif
917#if defined(HAS_INTERPOLATEROW_AVX2)
918  if (TestCpuFlag(kCpuHasAVX2)) {
919    InterpolateRow = InterpolateRow_Any_AVX2;
920    if (IS_ALIGNED(dst_width_bytes, 32)) {
921      InterpolateRow = InterpolateRow_AVX2;
922    }
923  }
924#endif
925#if defined(HAS_INTERPOLATEROW_NEON)
926  if (TestCpuFlag(kCpuHasNEON)) {
927    InterpolateRow = InterpolateRow_Any_NEON;
928    if (IS_ALIGNED(dst_width_bytes, 16)) {
929      InterpolateRow = InterpolateRow_NEON;
930    }
931  }
932#endif
933#if defined(HAS_INTERPOLATEROW_DSPR2)
934  if (TestCpuFlag(kCpuHasDSPR2) &&
935      IS_ALIGNED(src_argb, 4) && IS_ALIGNED(src_stride, 4) &&
936      IS_ALIGNED(dst_argb, 4) && IS_ALIGNED(dst_stride, 4)) {
937    InterpolateRow = InterpolateRow_Any_DSPR2;
938    if (IS_ALIGNED(dst_width_bytes, 4)) {
939      InterpolateRow = InterpolateRow_DSPR2;
940    }
941  }
942#endif
943  for (j = 0; j < dst_height; ++j) {
944    int yi;
945    int yf;
946    if (y > max_y) {
947      y = max_y;
948    }
949    yi = y >> 16;
950    yf = filtering ? ((y >> 8) & 255) : 0;
951    InterpolateRow(dst_argb, src_argb + yi * src_stride,
952                   src_stride, dst_width_bytes, yf);
953    dst_argb += dst_stride;
954    y += dy;
955  }
956}
957void ScalePlaneVertical_16(int src_height,
958                           int dst_width, int dst_height,
959                           int src_stride, int dst_stride,
960                           const uint16* src_argb, uint16* dst_argb,
961                           int x, int y, int dy,
962                           int wpp, enum FilterMode filtering) {
963  // TODO(fbarchard): Allow higher wpp.
964  int dst_width_words = dst_width * wpp;
965  void (*InterpolateRow)(uint16* dst_argb, const uint16* src_argb,
966      ptrdiff_t src_stride, int dst_width, int source_y_fraction) =
967      InterpolateRow_16_C;
968  const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
969  int j;
970  assert(wpp >= 1 && wpp <= 2);
971  assert(src_height != 0);
972  assert(dst_width > 0);
973  assert(dst_height > 0);
974  src_argb += (x >> 16) * wpp;
975#if defined(HAS_INTERPOLATEROW_16_SSE2)
976  if (TestCpuFlag(kCpuHasSSE2)) {
977    InterpolateRow = InterpolateRow_Any_16_SSE2;
978    if (IS_ALIGNED(dst_width_bytes, 16)) {
979      InterpolateRow = InterpolateRow_16_SSE2;
980    }
981  }
982#endif
983#if defined(HAS_INTERPOLATEROW_16_SSSE3)
984  if (TestCpuFlag(kCpuHasSSSE3)) {
985    InterpolateRow = InterpolateRow_Any_16_SSSE3;
986    if (IS_ALIGNED(dst_width_bytes, 16)) {
987      InterpolateRow = InterpolateRow_16_SSSE3;
988    }
989  }
990#endif
991#if defined(HAS_INTERPOLATEROW_16_AVX2)
992  if (TestCpuFlag(kCpuHasAVX2)) {
993    InterpolateRow = InterpolateRow_Any_16_AVX2;
994    if (IS_ALIGNED(dst_width_bytes, 32)) {
995      InterpolateRow = InterpolateRow_16_AVX2;
996    }
997  }
998#endif
999#if defined(HAS_INTERPOLATEROW_16_NEON)
1000  if (TestCpuFlag(kCpuHasNEON)) {
1001    InterpolateRow = InterpolateRow_Any_16_NEON;
1002    if (IS_ALIGNED(dst_width_bytes, 16)) {
1003      InterpolateRow = InterpolateRow_16_NEON;
1004    }
1005  }
1006#endif
1007#if defined(HAS_INTERPOLATEROW_16_DSPR2)
1008  if (TestCpuFlag(kCpuHasDSPR2) &&
1009      IS_ALIGNED(src_argb, 4) && IS_ALIGNED(src_stride, 4) &&
1010      IS_ALIGNED(dst_argb, 4) && IS_ALIGNED(dst_stride, 4)) {
1011    InterpolateRow = InterpolateRow_Any_16_DSPR2;
1012    if (IS_ALIGNED(dst_width_bytes, 4)) {
1013      InterpolateRow = InterpolateRow_16_DSPR2;
1014    }
1015  }
1016#endif
1017  for (j = 0; j < dst_height; ++j) {
1018    int yi;
1019    int yf;
1020    if (y > max_y) {
1021      y = max_y;
1022    }
1023    yi = y >> 16;
1024    yf = filtering ? ((y >> 8) & 255) : 0;
1025    InterpolateRow(dst_argb, src_argb + yi * src_stride,
1026                   src_stride, dst_width_words, yf);
1027    dst_argb += dst_stride;
1028    y += dy;
1029  }
1030}
1031
1032// Simplify the filtering based on scale factors.
1033enum FilterMode ScaleFilterReduce(int src_width, int src_height,
1034                                  int dst_width, int dst_height,
1035                                  enum FilterMode filtering) {
1036  if (src_width < 0) {
1037    src_width = -src_width;
1038  }
1039  if (src_height < 0) {
1040    src_height = -src_height;
1041  }
1042  if (filtering == kFilterBox) {
1043    // If scaling both axis to 0.5 or larger, switch from Box to Bilinear.
1044    if (dst_width * 2 >= src_width && dst_height * 2 >= src_height) {
1045      filtering = kFilterBilinear;
1046    }
1047  }
1048  if (filtering == kFilterBilinear) {
1049    if (src_height == 1) {
1050      filtering = kFilterLinear;
1051    }
1052    // TODO(fbarchard): Detect any odd scale factor and reduce to Linear.
1053    if (dst_height == src_height || dst_height * 3 == src_height) {
1054      filtering = kFilterLinear;
1055    }
1056    // TODO(fbarchard): Remove 1 pixel wide filter restriction, which is to
1057    // avoid reading 2 pixels horizontally that causes memory exception.
1058    if (src_width == 1) {
1059      filtering = kFilterNone;
1060    }
1061  }
1062  if (filtering == kFilterLinear) {
1063    if (src_width == 1) {
1064      filtering = kFilterNone;
1065    }
1066    // TODO(fbarchard): Detect any odd scale factor and reduce to None.
1067    if (dst_width == src_width || dst_width * 3 == src_width) {
1068      filtering = kFilterNone;
1069    }
1070  }
1071  return filtering;
1072}
1073
1074// Divide num by div and return as 16.16 fixed point result.
1075int FixedDiv_C(int num, int div) {
1076  return (int)(((int64)(num) << 16) / div);
1077}
1078
1079// Divide num by div and return as 16.16 fixed point result.
1080int FixedDiv1_C(int num, int div) {
1081  return (int)((((int64)(num) << 16) - 0x00010001) /
1082                          (div - 1));
1083}
1084
1085#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s)
1086
1087// Compute slope values for stepping.
1088void ScaleSlope(int src_width, int src_height,
1089                int dst_width, int dst_height,
1090                enum FilterMode filtering,
1091                int* x, int* y, int* dx, int* dy) {
1092  assert(x != NULL);
1093  assert(y != NULL);
1094  assert(dx != NULL);
1095  assert(dy != NULL);
1096  assert(src_width != 0);
1097  assert(src_height != 0);
1098  assert(dst_width > 0);
1099  assert(dst_height > 0);
1100  // Check for 1 pixel and avoid FixedDiv overflow.
1101  if (dst_width == 1 && src_width >= 32768) {
1102    dst_width = src_width;
1103  }
1104  if (dst_height == 1 && src_height >= 32768) {
1105    dst_height = src_height;
1106  }
1107  if (filtering == kFilterBox) {
1108    // Scale step for point sampling duplicates all pixels equally.
1109    *dx = FixedDiv(Abs(src_width), dst_width);
1110    *dy = FixedDiv(src_height, dst_height);
1111    *x = 0;
1112    *y = 0;
1113  } else if (filtering == kFilterBilinear) {
1114    // Scale step for bilinear sampling renders last pixel once for upsample.
1115    if (dst_width <= Abs(src_width)) {
1116      *dx = FixedDiv(Abs(src_width), dst_width);
1117      *x = CENTERSTART(*dx, -32768);  // Subtract 0.5 (32768) to center filter.
1118    } else if (dst_width > 1) {
1119      *dx = FixedDiv1(Abs(src_width), dst_width);
1120      *x = 0;
1121    }
1122    if (dst_height <= src_height) {
1123      *dy = FixedDiv(src_height,  dst_height);
1124      *y = CENTERSTART(*dy, -32768);  // Subtract 0.5 (32768) to center filter.
1125    } else if (dst_height > 1) {
1126      *dy = FixedDiv1(src_height, dst_height);
1127      *y = 0;
1128    }
1129  } else if (filtering == kFilterLinear) {
1130    // Scale step for bilinear sampling renders last pixel once for upsample.
1131    if (dst_width <= Abs(src_width)) {
1132      *dx = FixedDiv(Abs(src_width), dst_width);
1133      *x = CENTERSTART(*dx, -32768);  // Subtract 0.5 (32768) to center filter.
1134    } else if (dst_width > 1) {
1135      *dx = FixedDiv1(Abs(src_width), dst_width);
1136      *x = 0;
1137    }
1138    *dy = FixedDiv(src_height, dst_height);
1139    *y = *dy >> 1;
1140  } else {
1141    // Scale step for point sampling duplicates all pixels equally.
1142    *dx = FixedDiv(Abs(src_width), dst_width);
1143    *dy = FixedDiv(src_height, dst_height);
1144    *x = CENTERSTART(*dx, 0);
1145    *y = CENTERSTART(*dy, 0);
1146  }
1147  // Negative src_width means horizontally mirror.
1148  if (src_width < 0) {
1149    *x += (dst_width - 1) * *dx;
1150    *dx = -*dx;
1151    // src_width = -src_width;   // Caller must do this.
1152  }
1153}
1154#undef CENTERSTART
1155
1156#ifdef __cplusplus
1157}  // extern "C"
1158}  // namespace libyuv
1159#endif
1160