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#include <stdlib.h>  // For getenv()
16
17#include "libyuv/cpu_id.h"
18#include "libyuv/planar_functions.h"  // For CopyARGB
19#include "libyuv/row.h"
20
21#ifdef __cplusplus
22namespace libyuv {
23extern "C" {
24#endif
25
26// Bilinear SSE2 is disabled.
27#define SSE2_DISABLED 1
28
29// ARGB scaling uses bilinear or point, but not box filter.
30/**
31 * SSE2 downscalers with bilinear interpolation.
32 */
33
34#if !defined(YUV_DISABLE_ASM) && defined(_M_IX86)
35
36#define HAS_SCALEARGBROWDOWN2_SSE2
37// Reads 8 pixels, throws half away and writes 4 even pixels (0, 2, 4, 6)
38// Alignment requirement: src_ptr 16 byte aligned, dst_ptr 16 byte aligned.
39__declspec(naked) __declspec(align(16))
40static void ScaleARGBRowDown2_SSE2(const uint8* src_ptr,
41                                   ptrdiff_t /* src_stride */,
42                                   uint8* dst_ptr, int dst_width) {
43  __asm {
44    mov        eax, [esp + 4]        // src_ptr
45                                     // src_stride ignored
46    mov        edx, [esp + 12]       // dst_ptr
47    mov        ecx, [esp + 16]       // dst_width
48
49    align      16
50  wloop:
51    movdqa     xmm0, [eax]
52    movdqa     xmm1, [eax + 16]
53    lea        eax,  [eax + 32]
54    shufps     xmm0, xmm1, 0x88
55    sub        ecx, 4
56    movdqa     [edx], xmm0
57    lea        edx, [edx + 16]
58    jg         wloop
59
60    ret
61  }
62}
63
64// Blends 8x2 rectangle to 4x1.
65// Alignment requirement: src_ptr 16 byte aligned, dst_ptr 16 byte aligned.
66__declspec(naked) __declspec(align(16))
67static void ScaleARGBRowDown2Int_SSE2(const uint8* src_ptr,
68                                      ptrdiff_t src_stride,
69                                      uint8* dst_ptr, int dst_width) {
70  __asm {
71    push       esi
72    mov        eax, [esp + 4 + 4]    // src_ptr
73    mov        esi, [esp + 4 + 8]    // src_stride
74    mov        edx, [esp + 4 + 12]   // dst_ptr
75    mov        ecx, [esp + 4 + 16]   // dst_width
76
77    align      16
78  wloop:
79    movdqa     xmm0, [eax]
80    movdqa     xmm1, [eax + 16]
81    movdqa     xmm2, [eax + esi]
82    movdqa     xmm3, [eax + esi + 16]
83    lea        eax,  [eax + 32]
84    pavgb      xmm0, xmm2            // average rows
85    pavgb      xmm1, xmm3
86    movdqa     xmm2, xmm0            // average columns (8 to 4 pixels)
87    shufps     xmm0, xmm1, 0x88      // even pixels
88    shufps     xmm2, xmm1, 0xdd      // odd pixels
89    pavgb      xmm0, xmm2
90    sub        ecx, 4
91    movdqa     [edx], xmm0
92    lea        edx, [edx + 16]
93    jg         wloop
94
95    pop        esi
96    ret
97  }
98}
99
100#define HAS_SCALEARGBROWDOWNEVEN_SSE2
101// Reads 4 pixels at a time.
102// Alignment requirement: dst_ptr 16 byte aligned.
103__declspec(naked) __declspec(align(16))
104void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, ptrdiff_t src_stride,
105                               int src_stepx,
106                               uint8* dst_ptr, int dst_width) {
107  __asm {
108    push       ebx
109    push       edi
110    mov        eax, [esp + 8 + 4]    // src_ptr
111                                     // src_stride ignored
112    mov        ebx, [esp + 8 + 12]   // src_stepx
113    mov        edx, [esp + 8 + 16]   // dst_ptr
114    mov        ecx, [esp + 8 + 20]   // dst_width
115    lea        ebx, [ebx * 4]
116    lea        edi, [ebx + ebx * 2]
117
118    align      16
119  wloop:
120    movd       xmm0, [eax]
121    movd       xmm1, [eax + ebx]
122    punpckldq  xmm0, xmm1
123    movd       xmm2, [eax + ebx * 2]
124    movd       xmm3, [eax + edi]
125    lea        eax,  [eax + ebx * 4]
126    punpckldq  xmm2, xmm3
127    punpcklqdq xmm0, xmm2
128    sub        ecx, 4
129    movdqa     [edx], xmm0
130    lea        edx, [edx + 16]
131    jg         wloop
132
133    pop        edi
134    pop        ebx
135    ret
136  }
137}
138
139// Blends four 2x2 to 4x1.
140// Alignment requirement: dst_ptr 16 byte aligned.
141__declspec(naked) __declspec(align(16))
142static void ScaleARGBRowDownEvenInt_SSE2(const uint8* src_ptr,
143                                         ptrdiff_t src_stride,
144                                         int src_stepx,
145                                         uint8* dst_ptr, int dst_width) {
146  __asm {
147    push       ebx
148    push       esi
149    push       edi
150    mov        eax, [esp + 12 + 4]    // src_ptr
151    mov        esi, [esp + 12 + 8]    // src_stride
152    mov        ebx, [esp + 12 + 12]   // src_stepx
153    mov        edx, [esp + 12 + 16]   // dst_ptr
154    mov        ecx, [esp + 12 + 20]   // dst_width
155    lea        esi, [eax + esi]      // row1 pointer
156    lea        ebx, [ebx * 4]
157    lea        edi, [ebx + ebx * 2]
158
159    align      16
160  wloop:
161    movq       xmm0, qword ptr [eax] // row0 4 pairs
162    movhps     xmm0, qword ptr [eax + ebx]
163    movq       xmm1, qword ptr [eax + ebx * 2]
164    movhps     xmm1, qword ptr [eax + edi]
165    lea        eax,  [eax + ebx * 4]
166    movq       xmm2, qword ptr [esi] // row1 4 pairs
167    movhps     xmm2, qword ptr [esi + ebx]
168    movq       xmm3, qword ptr [esi + ebx * 2]
169    movhps     xmm3, qword ptr [esi + edi]
170    lea        esi,  [esi + ebx * 4]
171    pavgb      xmm0, xmm2            // average rows
172    pavgb      xmm1, xmm3
173    movdqa     xmm2, xmm0            // average columns (8 to 4 pixels)
174    shufps     xmm0, xmm1, 0x88      // even pixels
175    shufps     xmm2, xmm1, 0xdd      // odd pixels
176    pavgb      xmm0, xmm2
177    sub        ecx, 4
178    movdqa     [edx], xmm0
179    lea        edx, [edx + 16]
180    jg         wloop
181
182    pop        edi
183    pop        esi
184    pop        ebx
185    ret
186  }
187}
188
189// Bilinear row filtering combines 4x2 -> 4x1. SSE2 version.
190#ifndef SSE2_DISABLED
191#define HAS_SCALEARGBFILTERROWS_SSE2_DISABLED
192__declspec(naked) __declspec(align(16))
193void ScaleARGBFilterRows_SSE2(uint8* dst_ptr, const uint8* src_ptr,
194                              ptrdiff_t src_stride, int dst_width,
195                              int source_y_fraction) {
196  __asm {
197    push       esi
198    push       edi
199    mov        edi, [esp + 8 + 4]   // dst_ptr
200    mov        esi, [esp + 8 + 8]   // src_ptr
201    mov        edx, [esp + 8 + 12]  // src_stride
202    mov        ecx, [esp + 8 + 16]  // dst_width
203    mov        eax, [esp + 8 + 20]  // source_y_fraction (0..255)
204    sub        edi, esi
205    cmp        eax, 0
206    je         xloop1
207    cmp        eax, 128
208    je         xloop2
209
210    movd       xmm5, eax            // xmm5 = y fraction
211    punpcklbw  xmm5, xmm5
212    punpcklwd  xmm5, xmm5
213    pshufd     xmm5, xmm5, 0
214    pxor       xmm4, xmm4
215
216    // f * row1 + (1 - frac) row0
217    // frac * (row1 - row0) + row0
218    align      16
219  xloop:
220    movdqa     xmm0, [esi]  // row0
221    movdqa     xmm2, [esi + edx]  // row1
222    movdqa     xmm1, xmm0
223    movdqa     xmm3, xmm2
224    punpcklbw  xmm2, xmm4
225    punpckhbw  xmm3, xmm4
226    punpcklbw  xmm0, xmm4
227    punpckhbw  xmm1, xmm4
228    psubw      xmm2, xmm0  // row1 - row0
229    psubw      xmm3, xmm1
230    pmulhw     xmm2, xmm5  // scale diff
231    pmulhw     xmm3, xmm5
232    paddw      xmm0, xmm2  // sum rows
233    paddw      xmm1, xmm3
234    packuswb   xmm0, xmm1
235    sub        ecx, 4
236    movdqa     [esi + edi], xmm0
237    lea        esi, [esi + 16]
238    jg         xloop
239
240    shufps     xmm0, xmm0, 0xff
241    movdqa     [esi + edi], xmm0    // duplicate last pixel for filtering
242    pop        edi
243    pop        esi
244    ret
245
246    align      16
247  xloop1:
248    movdqa     xmm0, [esi]
249    sub        ecx, 4
250    movdqa     [esi + edi], xmm0
251    lea        esi, [esi + 16]
252    jg         xloop1
253
254    shufps     xmm0, xmm0, 0xff
255    movdqa     [esi + edi], xmm0
256    pop        edi
257    pop        esi
258    ret
259
260    align      16
261  xloop2:
262    movdqa     xmm0, [esi]
263    pavgb      xmm0, [esi + edx]
264    sub        ecx, 4
265    movdqa     [esi + edi], xmm0
266    lea        esi, [esi + 16]
267    jg         xloop2
268
269    shufps     xmm0, xmm0, 0xff
270    movdqa     [esi + edi], xmm0
271    pop        edi
272    pop        esi
273    ret
274  }
275}
276#endif  // SSE2_DISABLED
277
278// Bilinear row filtering combines 4x2 -> 4x1. SSSE3 version.
279#define HAS_SCALEARGBFILTERROWS_SSSE3
280__declspec(naked) __declspec(align(16))
281void ScaleARGBFilterRows_SSSE3(uint8* dst_ptr, const uint8* src_ptr,
282                               ptrdiff_t src_stride, int dst_width,
283                               int source_y_fraction) {
284  __asm {
285    push       esi
286    push       edi
287    mov        edi, [esp + 8 + 4]   // dst_ptr
288    mov        esi, [esp + 8 + 8]   // src_ptr
289    mov        edx, [esp + 8 + 12]  // src_stride
290    mov        ecx, [esp + 8 + 16]  // dst_width
291    mov        eax, [esp + 8 + 20]  // source_y_fraction (0..255)
292    sub        edi, esi
293    shr        eax, 1
294    cmp        eax, 0
295    je         xloop1
296    cmp        eax, 64
297    je         xloop2
298    movd       xmm0, eax  // high fraction 0..127
299    neg        eax
300    add        eax, 128
301    movd       xmm5, eax  // low fraction 128..1
302    punpcklbw  xmm5, xmm0
303    punpcklwd  xmm5, xmm5
304    pshufd     xmm5, xmm5, 0
305
306    align      16
307  xloop:
308    movdqa     xmm0, [esi]
309    movdqa     xmm2, [esi + edx]
310    movdqa     xmm1, xmm0
311    punpcklbw  xmm0, xmm2
312    punpckhbw  xmm1, xmm2
313    pmaddubsw  xmm0, xmm5
314    pmaddubsw  xmm1, xmm5
315    psrlw      xmm0, 7
316    psrlw      xmm1, 7
317    packuswb   xmm0, xmm1
318    sub        ecx, 4
319    movdqa     [esi + edi], xmm0
320    lea        esi, [esi + 16]
321    jg         xloop
322
323    shufps     xmm0, xmm0, 0xff
324    movdqa     [esi + edi], xmm0    // duplicate last pixel for filtering
325    pop        edi
326    pop        esi
327    ret
328
329    align      16
330  xloop1:
331    movdqa     xmm0, [esi]
332    sub        ecx, 4
333    movdqa     [esi + edi], xmm0
334    lea        esi, [esi + 16]
335    jg         xloop1
336
337    shufps     xmm0, xmm0, 0xff
338    movdqa     [esi + edi], xmm0
339    pop        edi
340    pop        esi
341    ret
342
343    align      16
344  xloop2:
345    movdqa     xmm0, [esi]
346    pavgb      xmm0, [esi + edx]
347    sub        ecx, 4
348    movdqa     [esi + edi], xmm0
349    lea        esi, [esi + 16]
350    jg         xloop2
351
352    shufps     xmm0, xmm0, 0xff
353    movdqa     [esi + edi], xmm0
354    pop        edi
355    pop        esi
356    ret
357  }
358}
359
360#elif !defined(YUV_DISABLE_ASM) && (defined(__x86_64__) || defined(__i386__))
361
362// GCC versions of row functions are verbatim conversions from Visual C.
363// Generated using gcc disassembly on Visual C object file:
364// objdump -D yuvscaler.obj >yuvscaler.txt
365#define HAS_SCALEARGBROWDOWN2_SSE2
366static void ScaleARGBRowDown2_SSE2(const uint8* src_ptr,
367                                   ptrdiff_t /* src_stride */,
368                                   uint8* dst_ptr, int dst_width) {
369  asm volatile (
370    ".p2align  4                               \n"
371  "1:                                          \n"
372    "movdqa    (%0),%%xmm0                     \n"
373    "movdqa    0x10(%0),%%xmm1                 \n"
374    "lea       0x20(%0),%0                     \n"
375    "shufps    $0x88,%%xmm1,%%xmm0             \n"
376    "sub       $0x4,%2                         \n"
377    "movdqa    %%xmm0,(%1)                     \n"
378    "lea       0x10(%1),%1                     \n"
379    "jg        1b                              \n"
380  : "+r"(src_ptr),   // %0
381    "+r"(dst_ptr),   // %1
382    "+r"(dst_width)  // %2
383  :
384  : "memory", "cc"
385#if defined(__SSE2__)
386    , "xmm0", "xmm1"
387#endif
388  );
389}
390
391static void ScaleARGBRowDown2Int_SSE2(const uint8* src_ptr,
392                                      ptrdiff_t src_stride,
393                                      uint8* dst_ptr, int dst_width) {
394  asm volatile (
395    ".p2align  4                               \n"
396  "1:                                          \n"
397    "movdqa    (%0),%%xmm0                     \n"
398    "movdqa    0x10(%0),%%xmm1                 \n"
399    "movdqa    (%0,%3,1),%%xmm2                \n"
400    "movdqa    0x10(%0,%3,1),%%xmm3            \n"
401    "lea       0x20(%0),%0                     \n"
402    "pavgb     %%xmm2,%%xmm0                   \n"
403    "pavgb     %%xmm3,%%xmm1                   \n"
404    "movdqa    %%xmm0,%%xmm2                   \n"
405    "shufps    $0x88,%%xmm1,%%xmm0             \n"
406    "shufps    $0xdd,%%xmm1,%%xmm2             \n"
407    "pavgb     %%xmm2,%%xmm0                   \n"
408    "sub       $0x4,%2                         \n"
409    "movdqa    %%xmm0,(%1)                     \n"
410    "lea       0x10(%1),%1                     \n"
411    "jg        1b                              \n"
412  : "+r"(src_ptr),    // %0
413    "+r"(dst_ptr),    // %1
414    "+r"(dst_width)   // %2
415  : "r"(static_cast<intptr_t>(src_stride))   // %3
416  : "memory", "cc"
417#if defined(__SSE2__)
418    , "xmm0", "xmm1", "xmm2", "xmm3"
419#endif
420  );
421}
422
423#define HAS_SCALEARGBROWDOWNEVEN_SSE2
424// Reads 4 pixels at a time.
425// Alignment requirement: dst_ptr 16 byte aligned.
426void ScaleARGBRowDownEven_SSE2(const uint8* src_ptr, ptrdiff_t src_stride,
427                               int src_stepx,
428                               uint8* dst_ptr, int dst_width) {
429  intptr_t src_stepx_x4 = static_cast<intptr_t>(src_stepx);
430  intptr_t src_stepx_x12 = 0;
431  asm volatile (
432    "lea       0x0(,%1,4),%1                   \n"
433    "lea       (%1,%1,2),%4                    \n"
434    ".p2align  4                               \n"
435  "1:                                          \n"
436    "movd      (%0),%%xmm0                     \n"
437    "movd      (%0,%1,1),%%xmm1                \n"
438    "punpckldq %%xmm1,%%xmm0                   \n"
439    "movd      (%0,%1,2),%%xmm2                \n"
440    "movd      (%0,%4,1),%%xmm3                \n"
441    "lea       (%0,%1,4),%0                    \n"
442    "punpckldq %%xmm3,%%xmm2                   \n"
443    "punpcklqdq %%xmm2,%%xmm0                  \n"
444    "sub       $0x4,%3                         \n"
445    "movdqa    %%xmm0,(%2)                     \n"
446    "lea       0x10(%2),%2                     \n"
447    "jg        1b                              \n"
448  : "+r"(src_ptr),       // %0
449    "+r"(src_stepx_x4),  // %1
450    "+r"(dst_ptr),       // %2
451    "+r"(dst_width),     // %3
452    "+r"(src_stepx_x12)  // %4
453  :
454  : "memory", "cc"
455#if defined(__SSE2__)
456    , "xmm0", "xmm1", "xmm2", "xmm3"
457#endif
458  );
459}
460
461// Blends four 2x2 to 4x1.
462// Alignment requirement: dst_ptr 16 byte aligned.
463static void ScaleARGBRowDownEvenInt_SSE2(const uint8* src_ptr,
464                                         ptrdiff_t src_stride, int src_stepx,
465                                         uint8* dst_ptr, int dst_width) {
466  intptr_t src_stepx_x4 = static_cast<intptr_t>(src_stepx);
467  intptr_t src_stepx_x12 = 0;
468  intptr_t row1 = static_cast<intptr_t>(src_stride);
469  asm volatile (
470    "lea       0x0(,%1,4),%1                   \n"
471    "lea       (%1,%1,2),%4                    \n"
472    "lea       (%0,%5,1),%5                    \n"
473    ".p2align  4                               \n"
474  "1:                                          \n"
475    "movq      (%0),%%xmm0                     \n"
476    "movhps    (%0,%1,1),%%xmm0                \n"
477    "movq      (%0,%1,2),%%xmm1                \n"
478    "movhps    (%0,%4,1),%%xmm1                \n"
479    "lea       (%0,%1,4),%0                    \n"
480    "movq      (%5),%%xmm2                     \n"
481    "movhps    (%5,%1,1),%%xmm2                \n"
482    "movq      (%5,%1,2),%%xmm3                \n"
483    "movhps    (%5,%4,1),%%xmm3                \n"
484    "lea       (%5,%1,4),%5                    \n"
485    "pavgb     %%xmm2,%%xmm0                   \n"
486    "pavgb     %%xmm3,%%xmm1                   \n"
487    "movdqa    %%xmm0,%%xmm2                   \n"
488    "shufps    $0x88,%%xmm1,%%xmm0             \n"
489    "shufps    $0xdd,%%xmm1,%%xmm2             \n"
490    "pavgb     %%xmm2,%%xmm0                   \n"
491    "sub       $0x4,%3                         \n"
492    "movdqa    %%xmm0,(%2)                     \n"
493    "lea       0x10(%2),%2                     \n"
494    "jg        1b                              \n"
495  : "+r"(src_ptr),        // %0
496    "+r"(src_stepx_x4),   // %1
497    "+r"(dst_ptr),        // %2
498    "+rm"(dst_width),     // %3
499    "+r"(src_stepx_x12),  // %4
500    "+r"(row1)            // %5
501  :
502  : "memory", "cc"
503#if defined(__SSE2__)
504    , "xmm0", "xmm1", "xmm2", "xmm3"
505#endif
506  );
507}
508
509#ifndef SSE2_DISABLED
510// Bilinear row filtering combines 4x2 -> 4x1. SSE2 version
511#define HAS_SCALEARGBFILTERROWS_SSE2_DISABLED
512void ScaleARGBFilterRows_SSE2(uint8* dst_ptr, const uint8* src_ptr,
513                              ptrdiff_t src_stride, int dst_width,
514                              int source_y_fraction) {
515  asm volatile (
516    "sub       %1,%0                           \n"
517    "cmp       $0x0,%3                         \n"
518    "je        2f                              \n"
519    "cmp       $0x80,%3                        \n"
520    "je        3f                              \n"
521    "movd      %3,%%xmm5                       \n"
522    "punpcklbw %%xmm5,%%xmm5                   \n"
523    "punpcklwd %%xmm5,%%xmm5                   \n"
524    "pshufd    $0x0,%%xmm5,%%xmm5              \n"
525    "pxor      %%xmm4,%%xmm4                   \n"
526    ".p2align  4                               \n"
527  "1:                                          \n"
528    "movdqa    (%1),%%xmm0                     \n"
529    "movdqa    (%1,%4,1),%%xmm2                \n"
530    "movdqa    %%xmm0,%%xmm1                   \n"
531    "movdqa    %%xmm2,%%xmm3                   \n"
532    "punpcklbw %%xmm4,%%xmm2                   \n"
533    "punpckhbw %%xmm4,%%xmm3                   \n"
534    "punpcklbw %%xmm4,%%xmm0                   \n"
535    "punpckhbw %%xmm4,%%xmm1                   \n"
536    "psubw     %%xmm0,%%xmm2                   \n"
537    "psubw     %%xmm1,%%xmm3                   \n"
538    "pmulhw    %%xmm5,%%xmm2                   \n"
539    "pmulhw    %%xmm5,%%xmm3                   \n"
540    "paddw     %%xmm2,%%xmm0                   \n"
541    "paddw     %%xmm3,%%xmm1                   \n"
542    "packuswb  %%xmm1,%%xmm0                   \n"
543    "sub       $0x4,%2                         \n"
544    "movdqa    %%xmm0,(%1,%0,1)                \n"
545    "lea       0x10(%1),%1                     \n"
546    "jg        1b                              \n"
547    "jmp       4f                              \n"
548    ".p2align  4                               \n"
549  "2:                                          \n"
550    "movdqa    (%1),%%xmm0                     \n"
551    "sub       $0x4,%2                         \n"
552    "movdqa    %%xmm0,(%1,%0,1)                \n"
553    "lea       0x10(%1),%1                     \n"
554    "jg        2b                              \n"
555    "jmp       4f                              \n"
556    ".p2align  4                               \n"
557  "3:                                          \n"
558    "movdqa    (%1),%%xmm0                     \n"
559    "pavgb     (%1,%4,1),%%xmm0                \n"
560    "sub       $0x4,%2                         \n"
561    "movdqa    %%xmm0,(%1,%0,1)                \n"
562    "lea       0x10(%1),%1                     \n"
563    "lea       0x10(%1),%1                     \n"
564    "jg        3b                              \n"
565    ".p2align  4                               \n"
566  "4:                                          \n"
567    "shufps    $0xff,%%xmm0,%%xmm0             \n"
568    "movdqa    %%xmm0,(%1,%0,1)                \n"
569  : "+r"(dst_ptr),     // %0
570    "+r"(src_ptr),     // %1
571    "+r"(dst_width),   // %2
572    "+r"(source_y_fraction)  // %3
573  : "r"(static_cast<intptr_t>(src_stride))  // %4
574  : "memory", "cc"
575#if defined(__SSE2__)
576    , "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"
577#endif
578  );
579}
580#endif  // SSE2_DISABLED
581
582// Bilinear row filtering combines 4x2 -> 4x1. SSSE3 version
583#define HAS_SCALEARGBFILTERROWS_SSSE3
584void ScaleARGBFilterRows_SSSE3(uint8* dst_ptr, const uint8* src_ptr,
585                               ptrdiff_t src_stride, int dst_width,
586                               int source_y_fraction) {
587  asm volatile (
588    "sub       %1,%0                           \n"
589    "shr       %3                              \n"
590    "cmp       $0x0,%3                         \n"
591    "je        2f                              \n"
592    "cmp       $0x40,%3                        \n"
593    "je        3f                              \n"
594    "movd      %3,%%xmm0                       \n"
595    "neg       %3                              \n"
596    "add       $0x80,%3                        \n"
597    "movd      %3,%%xmm5                       \n"
598    "punpcklbw %%xmm0,%%xmm5                   \n"
599    "punpcklwd %%xmm5,%%xmm5                   \n"
600    "pshufd    $0x0,%%xmm5,%%xmm5              \n"
601    ".p2align  4                               \n"
602  "1:                                          \n"
603    "movdqa    (%1),%%xmm0                     \n"
604    "movdqa    (%1,%4,1),%%xmm2                \n"
605    "movdqa    %%xmm0,%%xmm1                   \n"
606    "punpcklbw %%xmm2,%%xmm0                   \n"
607    "punpckhbw %%xmm2,%%xmm1                   \n"
608    "pmaddubsw %%xmm5,%%xmm0                   \n"
609    "pmaddubsw %%xmm5,%%xmm1                   \n"
610    "psrlw     $0x7,%%xmm0                     \n"
611    "psrlw     $0x7,%%xmm1                     \n"
612    "packuswb  %%xmm1,%%xmm0                   \n"
613    "sub       $0x4,%2                         \n"
614    "movdqa    %%xmm0,(%1,%0,1)                \n"
615    "lea       0x10(%1),%1                     \n"
616    "jg        1b                              \n"
617    "jmp       4f                              \n"
618    ".p2align  4                               \n"
619  "2:                                          \n"
620    "movdqa    (%1),%%xmm0                     \n"
621    "sub       $0x4,%2                         \n"
622    "movdqa    %%xmm0,(%1,%0,1)                \n"
623    "lea       0x10(%1),%1                     \n"
624    "jg        2b                              \n"
625    "jmp       4f                              \n"
626    ".p2align  4                               \n"
627  "3:                                          \n"
628    "movdqa    (%1),%%xmm0                     \n"
629    "pavgb     (%1,%4,1),%%xmm0                \n"
630    "sub       $0x4,%2                         \n"
631    "movdqa    %%xmm0,(%1,%0,1)                \n"
632    "lea       0x10(%1),%1                     \n"
633    "jg        3b                              \n"
634  "4:                                          \n"
635    ".p2align  4                               \n"
636    "shufps    $0xff,%%xmm0,%%xmm0             \n"
637    "movdqa    %%xmm0,(%1,%0,1)                \n"
638  : "+r"(dst_ptr),     // %0
639    "+r"(src_ptr),     // %1
640    "+r"(dst_width),   // %2
641    "+r"(source_y_fraction)  // %3
642  : "r"(static_cast<intptr_t>(src_stride))  // %4
643  : "memory", "cc"
644#if defined(__SSE2__)
645    , "xmm0", "xmm1", "xmm2", "xmm5"
646#endif
647  );
648}
649#endif  // defined(__x86_64__) || defined(__i386__)
650
651static void ScaleARGBRowDown2_C(const uint8* src_ptr,
652                                ptrdiff_t /* src_stride */,
653                                uint8* dst_ptr, int dst_width) {
654  const uint32* src = reinterpret_cast<const uint32*>(src_ptr);
655  uint32* dst = reinterpret_cast<uint32*>(dst_ptr);
656
657  for (int x = 0; x < dst_width - 1; x += 2) {
658    dst[0] = src[0];
659    dst[1] = src[2];
660    src += 4;
661    dst += 2;
662  }
663  if (dst_width & 1) {
664    dst[0] = src[0];
665  }
666}
667
668static void ScaleARGBRowDown2Int_C(const uint8* src_ptr, ptrdiff_t src_stride,
669                                   uint8* dst_ptr, int dst_width) {
670  for (int x = 0; x < dst_width; ++x) {
671    dst_ptr[0] = (src_ptr[0] + src_ptr[4] +
672                  src_ptr[src_stride] + src_ptr[src_stride + 4] + 2) >> 2;
673    dst_ptr[1] = (src_ptr[1] + src_ptr[5] +
674                  src_ptr[src_stride + 1] + src_ptr[src_stride + 5] + 2) >> 2;
675    dst_ptr[2] = (src_ptr[2] + src_ptr[6] +
676                  src_ptr[src_stride + 2] + src_ptr[src_stride + 6] + 2) >> 2;
677    dst_ptr[3] = (src_ptr[3] + src_ptr[7] +
678                  src_ptr[src_stride + 3] + src_ptr[src_stride + 7] + 2) >> 2;
679    src_ptr += 8;
680    dst_ptr += 4;
681  }
682}
683
684void ScaleARGBRowDownEven_C(const uint8* src_ptr, ptrdiff_t /* src_stride */,
685                            int src_stepx,
686                            uint8* dst_ptr, int dst_width) {
687  const uint32* src = reinterpret_cast<const uint32*>(src_ptr);
688  uint32* dst = reinterpret_cast<uint32*>(dst_ptr);
689
690  for (int x = 0; x < dst_width - 1; x += 2) {
691    dst[0] = src[0];
692    dst[1] = src[src_stepx];
693    src += src_stepx * 2;
694    dst += 2;
695  }
696  if (dst_width & 1) {
697    dst[0] = src[0];
698  }
699}
700
701static void ScaleARGBRowDownEvenInt_C(const uint8* src_ptr,
702                                      ptrdiff_t src_stride,
703                                      int src_stepx,
704                                      uint8* dst_ptr, int dst_width) {
705  for (int x = 0; x < dst_width; ++x) {
706    dst_ptr[0] = (src_ptr[0] + src_ptr[4] +
707                  src_ptr[src_stride] + src_ptr[src_stride + 4] + 2) >> 2;
708    dst_ptr[1] = (src_ptr[1] + src_ptr[5] +
709                  src_ptr[src_stride + 1] + src_ptr[src_stride + 5] + 2) >> 2;
710    dst_ptr[2] = (src_ptr[2] + src_ptr[6] +
711                  src_ptr[src_stride + 2] + src_ptr[src_stride + 6] + 2) >> 2;
712    dst_ptr[3] = (src_ptr[3] + src_ptr[7] +
713                  src_ptr[src_stride + 3] + src_ptr[src_stride + 7] + 2) >> 2;
714    src_ptr += src_stepx * 4;
715    dst_ptr += 4;
716  }
717}
718
719// (1-f)a + fb can be replaced with a + f(b-a)
720
721#define BLENDER1(a, b, f) (static_cast<int>(a) + \
722    ((f) * (static_cast<int>(b) - static_cast<int>(a)) >> 16))
723
724#define BLENDERC(a, b, f, s) static_cast<uint32>( \
725    BLENDER1(((a) >> s) & 255, ((b) >> s) & 255, f) << s)
726
727#define BLENDER(a, b, f) \
728    BLENDERC(a, b, f, 24) | BLENDERC(a, b, f, 16) | \
729    BLENDERC(a, b, f, 8) | BLENDERC(a, b, f, 0)
730
731static void ScaleARGBFilterCols_C(uint8* dst_ptr, const uint8* src_ptr,
732                                  int dst_width, int x, int dx) {
733  const uint32* src = reinterpret_cast<const uint32*>(src_ptr);
734  uint32* dst = reinterpret_cast<uint32*>(dst_ptr);
735  for (int j = 0; j < dst_width - 1; j += 2) {
736    int xi = x >> 16;
737    uint32 a = src[xi];
738    uint32 b = src[xi + 1];
739    dst[0] = BLENDER(a, b, x & 0xffff);
740    x += dx;
741    xi = x >> 16;
742    a = src[xi];
743    b = src[xi + 1];
744    dst[1] = BLENDER(a, b, x & 0xffff);
745    x += dx;
746    dst += 2;
747  }
748  if (dst_width & 1) {
749    int xi = x >> 16;
750    uint32 a = src[xi];
751    uint32 b = src[xi + 1];
752    dst[0] = BLENDER(a, b, x & 0xffff);
753  }
754}
755
756static const int kMaxInputWidth = 2560;
757
758// C version 2x2 -> 2x1
759void ScaleARGBFilterRows_C(uint8* dst_ptr, const uint8* src_ptr,
760                           ptrdiff_t src_stride,
761                           int dst_width, int source_y_fraction) {
762  assert(dst_width > 0);
763  int y1_fraction = source_y_fraction;
764  int y0_fraction = 256 - y1_fraction;
765  const uint8* src_ptr1 = src_ptr + src_stride;
766  uint8* end = dst_ptr + (dst_width << 2);
767  do {
768    dst_ptr[0] = (src_ptr[0] * y0_fraction + src_ptr1[0] * y1_fraction) >> 8;
769    dst_ptr[1] = (src_ptr[1] * y0_fraction + src_ptr1[1] * y1_fraction) >> 8;
770    dst_ptr[2] = (src_ptr[2] * y0_fraction + src_ptr1[2] * y1_fraction) >> 8;
771    dst_ptr[3] = (src_ptr[3] * y0_fraction + src_ptr1[3] * y1_fraction) >> 8;
772    dst_ptr[4] = (src_ptr[4] * y0_fraction + src_ptr1[4] * y1_fraction) >> 8;
773    dst_ptr[5] = (src_ptr[5] * y0_fraction + src_ptr1[5] * y1_fraction) >> 8;
774    dst_ptr[6] = (src_ptr[6] * y0_fraction + src_ptr1[6] * y1_fraction) >> 8;
775    dst_ptr[7] = (src_ptr[7] * y0_fraction + src_ptr1[7] * y1_fraction) >> 8;
776    src_ptr += 8;
777    src_ptr1 += 8;
778    dst_ptr += 8;
779  } while (dst_ptr < end);
780  // Duplicate the last pixel (4 bytes) for filtering.
781  dst_ptr[0] = dst_ptr[-4];
782  dst_ptr[1] = dst_ptr[-3];
783  dst_ptr[2] = dst_ptr[-2];
784  dst_ptr[3] = dst_ptr[-1];
785}
786
787/**
788 * ScaleARGB ARGB, 1/2
789 *
790 * This is an optimized version for scaling down a ARGB to 1/2 of
791 * its original size.
792 *
793 */
794static void ScaleARGBDown2(int /* src_width */, int /* src_height */,
795                           int dst_width, int dst_height,
796                           int src_stride, int dst_stride,
797                           const uint8* src_ptr, uint8* dst_ptr,
798                           FilterMode filtering) {
799  void (*ScaleARGBRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride,
800                            uint8* dst_ptr, int dst_width) =
801      filtering ? ScaleARGBRowDown2Int_C : ScaleARGBRowDown2_C;
802#if defined(HAS_SCALEARGBROWDOWN2_SSE2)
803  if (TestCpuFlag(kCpuHasSSE2) &&
804      IS_ALIGNED(dst_width, 4) &&
805      IS_ALIGNED(src_ptr, 16) && IS_ALIGNED(src_stride, 16) &&
806      IS_ALIGNED(dst_ptr, 16) && IS_ALIGNED(dst_stride, 16)) {
807    ScaleARGBRowDown2 = filtering ? ScaleARGBRowDown2Int_SSE2 :
808        ScaleARGBRowDown2_SSE2;
809  }
810#endif
811
812  // TODO(fbarchard): Loop through source height to allow odd height.
813  for (int y = 0; y < dst_height; ++y) {
814    ScaleARGBRowDown2(src_ptr, src_stride, dst_ptr, dst_width);
815    src_ptr += (src_stride << 1);
816    dst_ptr += dst_stride;
817  }
818}
819
820/**
821 * ScaleARGB ARGB Even
822 *
823 * This is an optimized version for scaling down a ARGB to even
824 * multiple of its original size.
825 *
826 */
827static void ScaleARGBDownEven(int src_width, int src_height,
828                              int dst_width, int dst_height,
829                              int src_stride, int dst_stride,
830                              const uint8* src_ptr, uint8* dst_ptr,
831                              FilterMode filtering) {
832  assert(IS_ALIGNED(src_width, 2));
833  assert(IS_ALIGNED(src_height, 2));
834  void (*ScaleARGBRowDownEven)(const uint8* src_ptr, ptrdiff_t src_stride,
835                               int src_step, uint8* dst_ptr, int dst_width) =
836      filtering ? ScaleARGBRowDownEvenInt_C : ScaleARGBRowDownEven_C;
837#if defined(HAS_SCALEARGBROWDOWNEVEN_SSE2)
838  if (TestCpuFlag(kCpuHasSSE2) &&
839      IS_ALIGNED(dst_width, 4) &&
840      IS_ALIGNED(dst_ptr, 16) && IS_ALIGNED(dst_stride, 16)) {
841    ScaleARGBRowDownEven = filtering ? ScaleARGBRowDownEvenInt_SSE2 :
842        ScaleARGBRowDownEven_SSE2;
843  }
844#endif
845  int src_step = src_width / dst_width;
846  // Adjust to point to center of box.
847  int row_step = src_height / dst_height;
848  int row_stride = row_step * src_stride;
849  src_ptr += ((row_step >> 1) - 1) * src_stride + ((src_step >> 1) - 1) * 4;
850  for (int y = 0; y < dst_height; ++y) {
851    ScaleARGBRowDownEven(src_ptr, src_stride, src_step, dst_ptr, dst_width);
852    src_ptr += row_stride;
853    dst_ptr += dst_stride;
854  }
855}
856/**
857 * ScaleARGB ARGB to/from any dimensions, with bilinear
858 * interpolation.
859 */
860
861static void ScaleARGBBilinear(int src_width, int src_height,
862                              int dst_width, int dst_height,
863                              int src_stride, int dst_stride,
864                              const uint8* src_ptr, uint8* dst_ptr) {
865  assert(dst_width > 0);
866  assert(dst_height > 0);
867  assert(src_width <= kMaxInputWidth);
868  SIMD_ALIGNED(uint8 row[kMaxInputWidth * 4 + 16]);
869  void (*ScaleARGBFilterRows)(uint8* dst_ptr, const uint8* src_ptr,
870                              ptrdiff_t src_stride,
871                              int dst_width, int source_y_fraction) =
872      ScaleARGBFilterRows_C;
873#if defined(HAS_SCALEARGBFILTERROWS_SSE2)
874  if (TestCpuFlag(kCpuHasSSE2) &&
875      IS_ALIGNED(src_stride, 16) && IS_ALIGNED(src_ptr, 16)) {
876    ScaleARGBFilterRows = ScaleARGBFilterRows_SSE2;
877  }
878#endif
879#if defined(HAS_SCALEARGBFILTERROWS_SSSE3)
880  if (TestCpuFlag(kCpuHasSSSE3) &&
881      IS_ALIGNED(src_stride, 16) && IS_ALIGNED(src_ptr, 16)) {
882    ScaleARGBFilterRows = ScaleARGBFilterRows_SSSE3;
883  }
884#endif
885  int dx = (src_width << 16) / dst_width;
886  int dy = (src_height << 16) / dst_height;
887  int x = (dx >= 65536) ? ((dx >> 1) - 32768) : (dx >> 1);
888  int y = (dy >= 65536) ? ((dy >> 1) - 32768) : (dy >> 1);
889  int maxy = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
890  for (int j = 0; j < dst_height; ++j) {
891    int yi = y >> 16;
892    int yf = (y >> 8) & 255;
893    const uint8* src = src_ptr + yi * src_stride;
894    ScaleARGBFilterRows(row, src, src_stride, src_width, yf);
895    ScaleARGBFilterCols_C(dst_ptr, row, dst_width, x, dx);
896    dst_ptr += dst_stride;
897    y += dy;
898    if (y > maxy) {
899      y = maxy;
900    }
901  }
902}
903
904// Scales a single row of pixels using point sampling.
905// Code is adapted from libyuv bilinear yuv scaling, but with bilinear
906//     interpolation off, and argb pixels instead of yuv.
907static void ScaleARGBCols(uint8* dst_ptr, const uint8* src_ptr,
908                          int dst_width, int x, int dx) {
909  const uint32* src = reinterpret_cast<const uint32*>(src_ptr);
910  uint32* dst = reinterpret_cast<uint32*>(dst_ptr);
911  for (int j = 0; j < dst_width - 1; j += 2) {
912    dst[0] = src[x >> 16];
913    x += dx;
914    dst[1] = src[x >> 16];
915    x += dx;
916    dst += 2;
917  }
918  if (dst_width & 1) {
919    dst[0] = src[x >> 16];
920  }
921}
922
923/**
924 * ScaleARGB ARGB to/from any dimensions, without interpolation.
925 * Fixed point math is used for performance: The upper 16 bits
926 * of x and dx is the integer part of the source position and
927 * the lower 16 bits are the fixed decimal part.
928 */
929
930static void ScaleARGBSimple(int src_width, int src_height,
931                            int dst_width, int dst_height,
932                            int src_stride, int dst_stride,
933                            const uint8* src_ptr, uint8* dst_ptr) {
934  int dx = (src_width << 16) / dst_width;
935  int dy = (src_height << 16) / dst_height;
936  int x = (dx >= 65536) ? ((dx >> 1) - 32768) : (dx >> 1);
937  int y = (dy >= 65536) ? ((dy >> 1) - 32768) : (dy >> 1);
938  for (int i = 0; i < dst_height; ++i) {
939    ScaleARGBCols(dst_ptr, src_ptr + (y >> 16) * src_stride, dst_width, x, dx);
940    dst_ptr += dst_stride;
941    y += dy;
942  }
943}
944
945/**
946 * ScaleARGB ARGB to/from any dimensions.
947 */
948static void ScaleARGBAnySize(int src_width, int src_height,
949                             int dst_width, int dst_height,
950                             int src_stride, int dst_stride,
951                             const uint8* src_ptr, uint8* dst_ptr,
952                             FilterMode filtering) {
953  if (!filtering || (src_width > kMaxInputWidth)) {
954    ScaleARGBSimple(src_width, src_height, dst_width, dst_height,
955                    src_stride, dst_stride, src_ptr, dst_ptr);
956  } else {
957    ScaleARGBBilinear(src_width, src_height, dst_width, dst_height,
958                      src_stride, dst_stride, src_ptr, dst_ptr);
959  }
960}
961
962// ScaleARGB a ARGB.
963//
964// This function in turn calls a scaling function
965// suitable for handling the desired resolutions.
966
967static void ScaleARGB(const uint8* src, int src_stride,
968                      int src_width, int src_height,
969                      uint8* dst, int dst_stride,
970                      int dst_width, int dst_height,
971                      FilterMode filtering) {
972#ifdef CPU_X86
973  // environment variable overrides for testing.
974  char *filter_override = getenv("LIBYUV_FILTER");
975  if (filter_override) {
976    filtering = (FilterMode)atoi(filter_override);  // NOLINT
977  }
978#endif
979  if (dst_width == src_width && dst_height == src_height) {
980    // Straight copy.
981    ARGBCopy(src, src_stride, dst, dst_stride, dst_width, dst_height);
982    return;
983  }
984  if (2 * dst_width == src_width && 2 * dst_height == src_height) {
985    // Optimized 1/2.
986    ScaleARGBDown2(src_width, src_height, dst_width, dst_height,
987                   src_stride, dst_stride, src, dst, filtering);
988    return;
989  }
990  int scale_down_x = src_width / dst_width;
991  int scale_down_y = src_height / dst_height;
992  if (dst_width * scale_down_x == src_width &&
993      dst_height * scale_down_y == src_height) {
994    if (!(scale_down_x & 1) && !(scale_down_y & 1)) {
995      // Optimized even scale down. ie 4, 6, 8, 10x
996      ScaleARGBDownEven(src_width, src_height, dst_width, dst_height,
997                        src_stride, dst_stride, src, dst, filtering);
998      return;
999    }
1000    if ((scale_down_x & 1) && (scale_down_y & 1)) {
1001      filtering = kFilterNone;
1002    }
1003  }
1004  // Arbitrary scale up and/or down.
1005  ScaleARGBAnySize(src_width, src_height, dst_width, dst_height,
1006                   src_stride, dst_stride, src, dst, filtering);
1007}
1008
1009// ScaleARGB an ARGB image.
1010LIBYUV_API
1011int ARGBScale(const uint8* src_argb, int src_stride_argb,
1012             int src_width, int src_height,
1013             uint8* dst_argb, int dst_stride_argb,
1014             int dst_width, int dst_height,
1015             FilterMode filtering) {
1016  if (!src_argb || src_width <= 0 || src_height == 0 ||
1017      !dst_argb || dst_width <= 0 || dst_height <= 0) {
1018    return -1;
1019  }
1020  // Negative height means invert the image.
1021  if (src_height < 0) {
1022    src_height = -src_height;
1023    src_argb = src_argb + (src_height - 1) * src_stride_argb;
1024    src_stride_argb = -src_stride_argb;
1025  }
1026  ScaleARGB(src_argb, src_stride_argb, src_width, src_height,
1027            dst_argb, dst_stride_argb, dst_width, dst_height,
1028            filtering);
1029  return 0;
1030}
1031
1032#ifdef __cplusplus
1033}  // extern "C"
1034}  // namespace libyuv
1035#endif
1036