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/convert.h"
12
13#include "libyuv/basic_types.h"
14#include "libyuv/cpu_id.h"
15#include "libyuv/planar_functions.h"
16#include "libyuv/rotate.h"
17#include "libyuv/scale.h"  // For ScalePlane()
18#include "libyuv/row.h"
19
20#ifdef __cplusplus
21namespace libyuv {
22extern "C" {
23#endif
24
25#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
26static __inline int Abs(int v) {
27  return v >= 0 ? v : -v;
28}
29
30// Any I4xx To I420 format with mirroring.
31static int I4xxToI420(const uint8* src_y, int src_stride_y,
32                      const uint8* src_u, int src_stride_u,
33                      const uint8* src_v, int src_stride_v,
34                      uint8* dst_y, int dst_stride_y,
35                      uint8* dst_u, int dst_stride_u,
36                      uint8* dst_v, int dst_stride_v,
37                      int src_y_width, int src_y_height,
38                      int src_uv_width, int src_uv_height) {
39  const int dst_y_width = Abs(src_y_width);
40  const int dst_y_height = Abs(src_y_height);
41  const int dst_uv_width = SUBSAMPLE(dst_y_width, 1, 1);
42  const int dst_uv_height = SUBSAMPLE(dst_y_height, 1, 1);
43  if (src_uv_width == 0 || src_uv_height == 0) {
44    return -1;
45  }
46  if (dst_y) {
47    ScalePlane(src_y, src_stride_y, src_y_width, src_y_height,
48               dst_y, dst_stride_y, dst_y_width, dst_y_height,
49               kFilterBilinear);
50  }
51  ScalePlane(src_u, src_stride_u, src_uv_width, src_uv_height,
52             dst_u, dst_stride_u, dst_uv_width, dst_uv_height,
53             kFilterBilinear);
54  ScalePlane(src_v, src_stride_v, src_uv_width, src_uv_height,
55             dst_v, dst_stride_v, dst_uv_width, dst_uv_height,
56             kFilterBilinear);
57  return 0;
58}
59
60// Copy I420 with optional flipping
61// TODO(fbarchard): Use Scale plane which supports mirroring, but ensure
62// is does row coalescing.
63LIBYUV_API
64int I420Copy(const uint8* src_y, int src_stride_y,
65             const uint8* src_u, int src_stride_u,
66             const uint8* src_v, int src_stride_v,
67             uint8* dst_y, int dst_stride_y,
68             uint8* dst_u, int dst_stride_u,
69             uint8* dst_v, int dst_stride_v,
70             int width, int height) {
71  int halfwidth = (width + 1) >> 1;
72  int halfheight = (height + 1) >> 1;
73  if (!src_u || !src_v ||
74      !dst_u || !dst_v ||
75      width <= 0 || height == 0) {
76    return -1;
77  }
78  // Negative height means invert the image.
79  if (height < 0) {
80    height = -height;
81    halfheight = (height + 1) >> 1;
82    src_y = src_y + (height - 1) * src_stride_y;
83    src_u = src_u + (halfheight - 1) * src_stride_u;
84    src_v = src_v + (halfheight - 1) * src_stride_v;
85    src_stride_y = -src_stride_y;
86    src_stride_u = -src_stride_u;
87    src_stride_v = -src_stride_v;
88  }
89
90  if (dst_y) {
91    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
92  }
93  // Copy UV planes.
94  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
95  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
96  return 0;
97}
98
99// 422 chroma is 1/2 width, 1x height
100// 420 chroma is 1/2 width, 1/2 height
101LIBYUV_API
102int I422ToI420(const uint8* src_y, int src_stride_y,
103               const uint8* src_u, int src_stride_u,
104               const uint8* src_v, int src_stride_v,
105               uint8* dst_y, int dst_stride_y,
106               uint8* dst_u, int dst_stride_u,
107               uint8* dst_v, int dst_stride_v,
108               int width, int height) {
109  const int src_uv_width = SUBSAMPLE(width, 1, 1);
110  return I4xxToI420(src_y, src_stride_y,
111                    src_u, src_stride_u,
112                    src_v, src_stride_v,
113                    dst_y, dst_stride_y,
114                    dst_u, dst_stride_u,
115                    dst_v, dst_stride_v,
116                    width, height,
117                    src_uv_width, height);
118}
119
120// 444 chroma is 1x width, 1x height
121// 420 chroma is 1/2 width, 1/2 height
122LIBYUV_API
123int I444ToI420(const uint8* src_y, int src_stride_y,
124               const uint8* src_u, int src_stride_u,
125               const uint8* src_v, int src_stride_v,
126               uint8* dst_y, int dst_stride_y,
127               uint8* dst_u, int dst_stride_u,
128               uint8* dst_v, int dst_stride_v,
129               int width, int height) {
130  return I4xxToI420(src_y, src_stride_y,
131                    src_u, src_stride_u,
132                    src_v, src_stride_v,
133                    dst_y, dst_stride_y,
134                    dst_u, dst_stride_u,
135                    dst_v, dst_stride_v,
136                    width, height,
137                    width, height);
138}
139
140// 411 chroma is 1/4 width, 1x height
141// 420 chroma is 1/2 width, 1/2 height
142LIBYUV_API
143int I411ToI420(const uint8* src_y, int src_stride_y,
144               const uint8* src_u, int src_stride_u,
145               const uint8* src_v, int src_stride_v,
146               uint8* dst_y, int dst_stride_y,
147               uint8* dst_u, int dst_stride_u,
148               uint8* dst_v, int dst_stride_v,
149               int width, int height) {
150  const int src_uv_width = SUBSAMPLE(width, 3, 2);
151  return I4xxToI420(src_y, src_stride_y,
152                    src_u, src_stride_u,
153                    src_v, src_stride_v,
154                    dst_y, dst_stride_y,
155                    dst_u, dst_stride_u,
156                    dst_v, dst_stride_v,
157                    width, height,
158                    src_uv_width, height);
159}
160
161// I400 is greyscale typically used in MJPG
162LIBYUV_API
163int I400ToI420(const uint8* src_y, int src_stride_y,
164               uint8* dst_y, int dst_stride_y,
165               uint8* dst_u, int dst_stride_u,
166               uint8* dst_v, int dst_stride_v,
167               int width, int height) {
168  int halfwidth = (width + 1) >> 1;
169  int halfheight = (height + 1) >> 1;
170  if (!dst_u || !dst_v ||
171      width <= 0 || height == 0) {
172    return -1;
173  }
174  // Negative height means invert the image.
175  if (height < 0) {
176    height = -height;
177    halfheight = (height + 1) >> 1;
178    src_y = src_y + (height - 1) * src_stride_y;
179    src_stride_y = -src_stride_y;
180  }
181  if (dst_y) {
182    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
183  }
184  SetPlane(dst_u, dst_stride_u, halfwidth, halfheight, 128);
185  SetPlane(dst_v, dst_stride_v, halfwidth, halfheight, 128);
186  return 0;
187}
188
189static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1,
190                       uint8* dst, int dst_stride,
191                       int width, int height) {
192  int y;
193  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
194#if defined(HAS_COPYROW_SSE2)
195  if (TestCpuFlag(kCpuHasSSE2)) {
196    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
197  }
198#endif
199#if defined(HAS_COPYROW_AVX)
200  if (TestCpuFlag(kCpuHasAVX)) {
201    CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
202  }
203#endif
204#if defined(HAS_COPYROW_ERMS)
205  if (TestCpuFlag(kCpuHasERMS)) {
206    CopyRow = CopyRow_ERMS;
207  }
208#endif
209#if defined(HAS_COPYROW_NEON)
210  if (TestCpuFlag(kCpuHasNEON)) {
211    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
212  }
213#endif
214#if defined(HAS_COPYROW_MIPS)
215  if (TestCpuFlag(kCpuHasMIPS)) {
216    CopyRow = CopyRow_MIPS;
217  }
218#endif
219
220  // Copy plane
221  for (y = 0; y < height - 1; y += 2) {
222    CopyRow(src, dst, width);
223    CopyRow(src + src_stride_0, dst + dst_stride, width);
224    src += src_stride_0 + src_stride_1;
225    dst += dst_stride * 2;
226  }
227  if (height & 1) {
228    CopyRow(src, dst, width);
229  }
230}
231
232// Support converting from FOURCC_M420
233// Useful for bandwidth constrained transports like USB 1.0 and 2.0 and for
234// easy conversion to I420.
235// M420 format description:
236// M420 is row biplanar 420: 2 rows of Y and 1 row of UV.
237// Chroma is half width / half height. (420)
238// src_stride_m420 is row planar. Normally this will be the width in pixels.
239//   The UV plane is half width, but 2 values, so src_stride_m420 applies to
240//   this as well as the two Y planes.
241static int X420ToI420(const uint8* src_y,
242                      int src_stride_y0, int src_stride_y1,
243                      const uint8* src_uv, int src_stride_uv,
244                      uint8* dst_y, int dst_stride_y,
245                      uint8* dst_u, int dst_stride_u,
246                      uint8* dst_v, int dst_stride_v,
247                      int width, int height) {
248  int halfwidth = (width + 1) >> 1;
249  int halfheight = (height + 1) >> 1;
250  if (!src_uv || !dst_u || !dst_v ||
251      width <= 0 || height == 0) {
252    return -1;
253  }
254  // Negative height means invert the image.
255  if (height < 0) {
256    height = -height;
257    halfheight = (height + 1) >> 1;
258    if (dst_y) {
259      dst_y = dst_y + (height - 1) * dst_stride_y;
260    }
261    dst_u = dst_u + (halfheight - 1) * dst_stride_u;
262    dst_v = dst_v + (halfheight - 1) * dst_stride_v;
263    dst_stride_y = -dst_stride_y;
264    dst_stride_u = -dst_stride_u;
265    dst_stride_v = -dst_stride_v;
266  }
267  // Coalesce rows.
268  if (src_stride_y0 == width &&
269      src_stride_y1 == width &&
270      dst_stride_y == width) {
271    width *= height;
272    height = 1;
273    src_stride_y0 = src_stride_y1 = dst_stride_y = 0;
274  }
275  // Coalesce rows.
276  if (src_stride_uv == halfwidth * 2 &&
277      dst_stride_u == halfwidth &&
278      dst_stride_v == halfwidth) {
279    halfwidth *= halfheight;
280    halfheight = 1;
281    src_stride_uv = dst_stride_u = dst_stride_v = 0;
282  }
283
284  if (dst_y) {
285    if (src_stride_y0 == src_stride_y1) {
286      CopyPlane(src_y, src_stride_y0, dst_y, dst_stride_y, width, height);
287    } else {
288      CopyPlane2(src_y, src_stride_y0, src_stride_y1, dst_y, dst_stride_y,
289                 width, height);
290    }
291  }
292
293  // Split UV plane - NV12 / NV21
294  SplitUVPlane(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v, dst_stride_v,
295               halfwidth, halfheight);
296
297  return 0;
298}
299
300// Convert NV12 to I420.
301LIBYUV_API
302int NV12ToI420(const uint8* src_y, int src_stride_y,
303               const uint8* src_uv, int src_stride_uv,
304               uint8* dst_y, int dst_stride_y,
305               uint8* dst_u, int dst_stride_u,
306               uint8* dst_v, int dst_stride_v,
307               int width, int height) {
308  return X420ToI420(src_y, src_stride_y, src_stride_y,
309                    src_uv, src_stride_uv,
310                    dst_y, dst_stride_y,
311                    dst_u, dst_stride_u,
312                    dst_v, dst_stride_v,
313                    width, height);
314}
315
316// Convert NV21 to I420.  Same as NV12 but u and v pointers swapped.
317LIBYUV_API
318int NV21ToI420(const uint8* src_y, int src_stride_y,
319               const uint8* src_vu, int src_stride_vu,
320               uint8* dst_y, int dst_stride_y,
321               uint8* dst_u, int dst_stride_u,
322               uint8* dst_v, int dst_stride_v,
323               int width, int height) {
324  return X420ToI420(src_y, src_stride_y, src_stride_y,
325                    src_vu, src_stride_vu,
326                    dst_y, dst_stride_y,
327                    dst_v, dst_stride_v,
328                    dst_u, dst_stride_u,
329                    width, height);
330}
331
332// Convert M420 to I420.
333LIBYUV_API
334int M420ToI420(const uint8* src_m420, int src_stride_m420,
335               uint8* dst_y, int dst_stride_y,
336               uint8* dst_u, int dst_stride_u,
337               uint8* dst_v, int dst_stride_v,
338               int width, int height) {
339  return X420ToI420(src_m420, src_stride_m420, src_stride_m420 * 2,
340                    src_m420 + src_stride_m420 * 2, src_stride_m420 * 3,
341                    dst_y, dst_stride_y,
342                    dst_u, dst_stride_u,
343                    dst_v, dst_stride_v,
344                    width, height);
345}
346
347// Convert YUY2 to I420.
348LIBYUV_API
349int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
350               uint8* dst_y, int dst_stride_y,
351               uint8* dst_u, int dst_stride_u,
352               uint8* dst_v, int dst_stride_v,
353               int width, int height) {
354  int y;
355  void (*YUY2ToUVRow)(const uint8* src_yuy2, int src_stride_yuy2,
356      uint8* dst_u, uint8* dst_v, int width) = YUY2ToUVRow_C;
357  void (*YUY2ToYRow)(const uint8* src_yuy2,
358      uint8* dst_y, int width) = YUY2ToYRow_C;
359  // Negative height means invert the image.
360  if (height < 0) {
361    height = -height;
362    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
363    src_stride_yuy2 = -src_stride_yuy2;
364  }
365#if defined(HAS_YUY2TOYROW_SSE2)
366  if (TestCpuFlag(kCpuHasSSE2)) {
367    YUY2ToUVRow = YUY2ToUVRow_Any_SSE2;
368    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
369    if (IS_ALIGNED(width, 16)) {
370      YUY2ToUVRow = YUY2ToUVRow_SSE2;
371      YUY2ToYRow = YUY2ToYRow_SSE2;
372    }
373  }
374#endif
375#if defined(HAS_YUY2TOYROW_AVX2)
376  if (TestCpuFlag(kCpuHasAVX2)) {
377    YUY2ToUVRow = YUY2ToUVRow_Any_AVX2;
378    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
379    if (IS_ALIGNED(width, 32)) {
380      YUY2ToUVRow = YUY2ToUVRow_AVX2;
381      YUY2ToYRow = YUY2ToYRow_AVX2;
382    }
383  }
384#endif
385#if defined(HAS_YUY2TOYROW_NEON)
386  if (TestCpuFlag(kCpuHasNEON)) {
387    YUY2ToYRow = YUY2ToYRow_Any_NEON;
388    YUY2ToUVRow = YUY2ToUVRow_Any_NEON;
389    if (IS_ALIGNED(width, 16)) {
390      YUY2ToYRow = YUY2ToYRow_NEON;
391      YUY2ToUVRow = YUY2ToUVRow_NEON;
392    }
393  }
394#endif
395
396  for (y = 0; y < height - 1; y += 2) {
397    YUY2ToUVRow(src_yuy2, src_stride_yuy2, dst_u, dst_v, width);
398    YUY2ToYRow(src_yuy2, dst_y, width);
399    YUY2ToYRow(src_yuy2 + src_stride_yuy2, dst_y + dst_stride_y, width);
400    src_yuy2 += src_stride_yuy2 * 2;
401    dst_y += dst_stride_y * 2;
402    dst_u += dst_stride_u;
403    dst_v += dst_stride_v;
404  }
405  if (height & 1) {
406    YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width);
407    YUY2ToYRow(src_yuy2, dst_y, width);
408  }
409  return 0;
410}
411
412// Convert UYVY to I420.
413LIBYUV_API
414int UYVYToI420(const uint8* src_uyvy, int src_stride_uyvy,
415               uint8* dst_y, int dst_stride_y,
416               uint8* dst_u, int dst_stride_u,
417               uint8* dst_v, int dst_stride_v,
418               int width, int height) {
419  int y;
420  void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy,
421      uint8* dst_u, uint8* dst_v, int width) = UYVYToUVRow_C;
422  void (*UYVYToYRow)(const uint8* src_uyvy,
423      uint8* dst_y, int width) = UYVYToYRow_C;
424  // Negative height means invert the image.
425  if (height < 0) {
426    height = -height;
427    src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
428    src_stride_uyvy = -src_stride_uyvy;
429  }
430#if defined(HAS_UYVYTOYROW_SSE2)
431  if (TestCpuFlag(kCpuHasSSE2)) {
432    UYVYToUVRow = UYVYToUVRow_Any_SSE2;
433    UYVYToYRow = UYVYToYRow_Any_SSE2;
434    if (IS_ALIGNED(width, 16)) {
435      UYVYToUVRow = UYVYToUVRow_SSE2;
436      UYVYToYRow = UYVYToYRow_SSE2;
437    }
438  }
439#endif
440#if defined(HAS_UYVYTOYROW_AVX2)
441  if (TestCpuFlag(kCpuHasAVX2)) {
442    UYVYToUVRow = UYVYToUVRow_Any_AVX2;
443    UYVYToYRow = UYVYToYRow_Any_AVX2;
444    if (IS_ALIGNED(width, 32)) {
445      UYVYToUVRow = UYVYToUVRow_AVX2;
446      UYVYToYRow = UYVYToYRow_AVX2;
447    }
448  }
449#endif
450#if defined(HAS_UYVYTOYROW_NEON)
451  if (TestCpuFlag(kCpuHasNEON)) {
452    UYVYToYRow = UYVYToYRow_Any_NEON;
453    UYVYToUVRow = UYVYToUVRow_Any_NEON;
454    if (IS_ALIGNED(width, 16)) {
455      UYVYToYRow = UYVYToYRow_NEON;
456      UYVYToUVRow = UYVYToUVRow_NEON;
457    }
458  }
459#endif
460
461  for (y = 0; y < height - 1; y += 2) {
462    UYVYToUVRow(src_uyvy, src_stride_uyvy, dst_u, dst_v, width);
463    UYVYToYRow(src_uyvy, dst_y, width);
464    UYVYToYRow(src_uyvy + src_stride_uyvy, dst_y + dst_stride_y, width);
465    src_uyvy += src_stride_uyvy * 2;
466    dst_y += dst_stride_y * 2;
467    dst_u += dst_stride_u;
468    dst_v += dst_stride_v;
469  }
470  if (height & 1) {
471    UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width);
472    UYVYToYRow(src_uyvy, dst_y, width);
473  }
474  return 0;
475}
476
477// Convert ARGB to I420.
478LIBYUV_API
479int ARGBToI420(const uint8* src_argb, int src_stride_argb,
480               uint8* dst_y, int dst_stride_y,
481               uint8* dst_u, int dst_stride_u,
482               uint8* dst_v, int dst_stride_v,
483               int width, int height) {
484  int y;
485  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
486      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
487  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
488      ARGBToYRow_C;
489  if (!src_argb ||
490      !dst_y || !dst_u || !dst_v ||
491      width <= 0 || height == 0) {
492    return -1;
493  }
494  // Negative height means invert the image.
495  if (height < 0) {
496    height = -height;
497    src_argb = src_argb + (height - 1) * src_stride_argb;
498    src_stride_argb = -src_stride_argb;
499  }
500#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
501  if (TestCpuFlag(kCpuHasSSSE3)) {
502    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
503    ARGBToYRow = ARGBToYRow_Any_SSSE3;
504    if (IS_ALIGNED(width, 16)) {
505      ARGBToUVRow = ARGBToUVRow_SSSE3;
506      ARGBToYRow = ARGBToYRow_SSSE3;
507    }
508  }
509#endif
510#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
511  if (TestCpuFlag(kCpuHasAVX2)) {
512    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
513    ARGBToYRow = ARGBToYRow_Any_AVX2;
514    if (IS_ALIGNED(width, 32)) {
515      ARGBToUVRow = ARGBToUVRow_AVX2;
516      ARGBToYRow = ARGBToYRow_AVX2;
517    }
518  }
519#endif
520#if defined(HAS_ARGBTOYROW_NEON)
521  if (TestCpuFlag(kCpuHasNEON)) {
522    ARGBToYRow = ARGBToYRow_Any_NEON;
523    if (IS_ALIGNED(width, 8)) {
524      ARGBToYRow = ARGBToYRow_NEON;
525    }
526  }
527#endif
528#if defined(HAS_ARGBTOUVROW_NEON)
529  if (TestCpuFlag(kCpuHasNEON)) {
530    ARGBToUVRow = ARGBToUVRow_Any_NEON;
531    if (IS_ALIGNED(width, 16)) {
532      ARGBToUVRow = ARGBToUVRow_NEON;
533    }
534  }
535#endif
536
537  for (y = 0; y < height - 1; y += 2) {
538    ARGBToUVRow(src_argb, src_stride_argb, dst_u, dst_v, width);
539    ARGBToYRow(src_argb, dst_y, width);
540    ARGBToYRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width);
541    src_argb += src_stride_argb * 2;
542    dst_y += dst_stride_y * 2;
543    dst_u += dst_stride_u;
544    dst_v += dst_stride_v;
545  }
546  if (height & 1) {
547    ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
548    ARGBToYRow(src_argb, dst_y, width);
549  }
550  return 0;
551}
552
553// Convert BGRA to I420.
554LIBYUV_API
555int BGRAToI420(const uint8* src_bgra, int src_stride_bgra,
556               uint8* dst_y, int dst_stride_y,
557               uint8* dst_u, int dst_stride_u,
558               uint8* dst_v, int dst_stride_v,
559               int width, int height) {
560  int y;
561  void (*BGRAToUVRow)(const uint8* src_bgra0, int src_stride_bgra,
562      uint8* dst_u, uint8* dst_v, int width) = BGRAToUVRow_C;
563  void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int width) =
564      BGRAToYRow_C;
565  if (!src_bgra ||
566      !dst_y || !dst_u || !dst_v ||
567      width <= 0 || height == 0) {
568    return -1;
569  }
570  // Negative height means invert the image.
571  if (height < 0) {
572    height = -height;
573    src_bgra = src_bgra + (height - 1) * src_stride_bgra;
574    src_stride_bgra = -src_stride_bgra;
575  }
576#if defined(HAS_BGRATOYROW_SSSE3) && defined(HAS_BGRATOUVROW_SSSE3)
577  if (TestCpuFlag(kCpuHasSSSE3)) {
578    BGRAToUVRow = BGRAToUVRow_Any_SSSE3;
579    BGRAToYRow = BGRAToYRow_Any_SSSE3;
580    if (IS_ALIGNED(width, 16)) {
581      BGRAToUVRow = BGRAToUVRow_SSSE3;
582      BGRAToYRow = BGRAToYRow_SSSE3;
583    }
584  }
585#endif
586#if defined(HAS_BGRATOYROW_NEON)
587  if (TestCpuFlag(kCpuHasNEON)) {
588    BGRAToYRow = BGRAToYRow_Any_NEON;
589    if (IS_ALIGNED(width, 8)) {
590      BGRAToYRow = BGRAToYRow_NEON;
591    }
592  }
593#endif
594#if defined(HAS_BGRATOUVROW_NEON)
595    if (TestCpuFlag(kCpuHasNEON)) {
596      BGRAToUVRow = BGRAToUVRow_Any_NEON;
597      if (IS_ALIGNED(width, 16)) {
598        BGRAToUVRow = BGRAToUVRow_NEON;
599      }
600    }
601#endif
602
603  for (y = 0; y < height - 1; y += 2) {
604    BGRAToUVRow(src_bgra, src_stride_bgra, dst_u, dst_v, width);
605    BGRAToYRow(src_bgra, dst_y, width);
606    BGRAToYRow(src_bgra + src_stride_bgra, dst_y + dst_stride_y, width);
607    src_bgra += src_stride_bgra * 2;
608    dst_y += dst_stride_y * 2;
609    dst_u += dst_stride_u;
610    dst_v += dst_stride_v;
611  }
612  if (height & 1) {
613    BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width);
614    BGRAToYRow(src_bgra, dst_y, width);
615  }
616  return 0;
617}
618
619// Convert ABGR to I420.
620LIBYUV_API
621int ABGRToI420(const uint8* src_abgr, int src_stride_abgr,
622               uint8* dst_y, int dst_stride_y,
623               uint8* dst_u, int dst_stride_u,
624               uint8* dst_v, int dst_stride_v,
625               int width, int height) {
626  int y;
627  void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr,
628      uint8* dst_u, uint8* dst_v, int width) = ABGRToUVRow_C;
629  void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int width) =
630      ABGRToYRow_C;
631  if (!src_abgr ||
632      !dst_y || !dst_u || !dst_v ||
633      width <= 0 || height == 0) {
634    return -1;
635  }
636  // Negative height means invert the image.
637  if (height < 0) {
638    height = -height;
639    src_abgr = src_abgr + (height - 1) * src_stride_abgr;
640    src_stride_abgr = -src_stride_abgr;
641  }
642#if defined(HAS_ABGRTOYROW_SSSE3) && defined(HAS_ABGRTOUVROW_SSSE3)
643  if (TestCpuFlag(kCpuHasSSSE3)) {
644    ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
645    ABGRToYRow = ABGRToYRow_Any_SSSE3;
646    if (IS_ALIGNED(width, 16)) {
647      ABGRToUVRow = ABGRToUVRow_SSSE3;
648      ABGRToYRow = ABGRToYRow_SSSE3;
649    }
650  }
651#endif
652#if defined(HAS_ABGRTOYROW_NEON)
653  if (TestCpuFlag(kCpuHasNEON)) {
654    ABGRToYRow = ABGRToYRow_Any_NEON;
655    if (IS_ALIGNED(width, 8)) {
656      ABGRToYRow = ABGRToYRow_NEON;
657    }
658  }
659#endif
660#if defined(HAS_ABGRTOUVROW_NEON)
661  if (TestCpuFlag(kCpuHasNEON)) {
662    ABGRToUVRow = ABGRToUVRow_Any_NEON;
663    if (IS_ALIGNED(width, 16)) {
664      ABGRToUVRow = ABGRToUVRow_NEON;
665    }
666  }
667#endif
668
669  for (y = 0; y < height - 1; y += 2) {
670    ABGRToUVRow(src_abgr, src_stride_abgr, dst_u, dst_v, width);
671    ABGRToYRow(src_abgr, dst_y, width);
672    ABGRToYRow(src_abgr + src_stride_abgr, dst_y + dst_stride_y, width);
673    src_abgr += src_stride_abgr * 2;
674    dst_y += dst_stride_y * 2;
675    dst_u += dst_stride_u;
676    dst_v += dst_stride_v;
677  }
678  if (height & 1) {
679    ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
680    ABGRToYRow(src_abgr, dst_y, width);
681  }
682  return 0;
683}
684
685// Convert RGBA to I420.
686LIBYUV_API
687int RGBAToI420(const uint8* src_rgba, int src_stride_rgba,
688               uint8* dst_y, int dst_stride_y,
689               uint8* dst_u, int dst_stride_u,
690               uint8* dst_v, int dst_stride_v,
691               int width, int height) {
692  int y;
693  void (*RGBAToUVRow)(const uint8* src_rgba0, int src_stride_rgba,
694      uint8* dst_u, uint8* dst_v, int width) = RGBAToUVRow_C;
695  void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int width) =
696      RGBAToYRow_C;
697  if (!src_rgba ||
698      !dst_y || !dst_u || !dst_v ||
699      width <= 0 || height == 0) {
700    return -1;
701  }
702  // Negative height means invert the image.
703  if (height < 0) {
704    height = -height;
705    src_rgba = src_rgba + (height - 1) * src_stride_rgba;
706    src_stride_rgba = -src_stride_rgba;
707  }
708#if defined(HAS_RGBATOYROW_SSSE3) && defined(HAS_RGBATOUVROW_SSSE3)
709  if (TestCpuFlag(kCpuHasSSSE3)) {
710    RGBAToUVRow = RGBAToUVRow_Any_SSSE3;
711    RGBAToYRow = RGBAToYRow_Any_SSSE3;
712    if (IS_ALIGNED(width, 16)) {
713      RGBAToUVRow = RGBAToUVRow_SSSE3;
714      RGBAToYRow = RGBAToYRow_SSSE3;
715    }
716  }
717#endif
718#if defined(HAS_RGBATOYROW_NEON)
719  if (TestCpuFlag(kCpuHasNEON)) {
720    RGBAToYRow = RGBAToYRow_Any_NEON;
721    if (IS_ALIGNED(width, 8)) {
722      RGBAToYRow = RGBAToYRow_NEON;
723    }
724  }
725#endif
726#if defined(HAS_RGBATOUVROW_NEON)
727  if (TestCpuFlag(kCpuHasNEON)) {
728    RGBAToUVRow = RGBAToUVRow_Any_NEON;
729    if (IS_ALIGNED(width, 16)) {
730      RGBAToUVRow = RGBAToUVRow_NEON;
731    }
732  }
733#endif
734
735  for (y = 0; y < height - 1; y += 2) {
736    RGBAToUVRow(src_rgba, src_stride_rgba, dst_u, dst_v, width);
737    RGBAToYRow(src_rgba, dst_y, width);
738    RGBAToYRow(src_rgba + src_stride_rgba, dst_y + dst_stride_y, width);
739    src_rgba += src_stride_rgba * 2;
740    dst_y += dst_stride_y * 2;
741    dst_u += dst_stride_u;
742    dst_v += dst_stride_v;
743  }
744  if (height & 1) {
745    RGBAToUVRow(src_rgba, 0, dst_u, dst_v, width);
746    RGBAToYRow(src_rgba, dst_y, width);
747  }
748  return 0;
749}
750
751// Convert RGB24 to I420.
752LIBYUV_API
753int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24,
754                uint8* dst_y, int dst_stride_y,
755                uint8* dst_u, int dst_stride_u,
756                uint8* dst_v, int dst_stride_v,
757                int width, int height) {
758  int y;
759#if defined(HAS_RGB24TOYROW_NEON)
760  void (*RGB24ToUVRow)(const uint8* src_rgb24, int src_stride_rgb24,
761      uint8* dst_u, uint8* dst_v, int width) = RGB24ToUVRow_C;
762  void (*RGB24ToYRow)(const uint8* src_rgb24, uint8* dst_y, int width) =
763      RGB24ToYRow_C;
764#else
765  void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
766      RGB24ToARGBRow_C;
767  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
768      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
769  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
770      ARGBToYRow_C;
771#endif
772  if (!src_rgb24 || !dst_y || !dst_u || !dst_v ||
773      width <= 0 || height == 0) {
774    return -1;
775  }
776  // Negative height means invert the image.
777  if (height < 0) {
778    height = -height;
779    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
780    src_stride_rgb24 = -src_stride_rgb24;
781  }
782
783// Neon version does direct RGB24 to YUV.
784#if defined(HAS_RGB24TOYROW_NEON)
785  if (TestCpuFlag(kCpuHasNEON)) {
786    RGB24ToUVRow = RGB24ToUVRow_Any_NEON;
787    RGB24ToYRow = RGB24ToYRow_Any_NEON;
788    if (IS_ALIGNED(width, 8)) {
789      RGB24ToYRow = RGB24ToYRow_NEON;
790      if (IS_ALIGNED(width, 16)) {
791        RGB24ToUVRow = RGB24ToUVRow_NEON;
792      }
793    }
794  }
795// Other platforms do intermediate conversion from RGB24 to ARGB.
796#else
797#if defined(HAS_RGB24TOARGBROW_SSSE3)
798  if (TestCpuFlag(kCpuHasSSSE3)) {
799    RGB24ToARGBRow = RGB24ToARGBRow_Any_SSSE3;
800    if (IS_ALIGNED(width, 16)) {
801      RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
802    }
803  }
804#endif
805#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
806  if (TestCpuFlag(kCpuHasSSSE3)) {
807    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
808    ARGBToYRow = ARGBToYRow_Any_SSSE3;
809    if (IS_ALIGNED(width, 16)) {
810      ARGBToUVRow = ARGBToUVRow_SSSE3;
811      ARGBToYRow = ARGBToYRow_SSSE3;
812    }
813  }
814#endif
815#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
816  if (TestCpuFlag(kCpuHasAVX2)) {
817    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
818    ARGBToYRow = ARGBToYRow_Any_AVX2;
819    if (IS_ALIGNED(width, 32)) {
820      ARGBToUVRow = ARGBToUVRow_AVX2;
821      ARGBToYRow = ARGBToYRow_AVX2;
822    }
823  }
824#endif
825  {
826    // Allocate 2 rows of ARGB.
827    const int kRowSize = (width * 4 + 31) & ~31;
828    align_buffer_64(row, kRowSize * 2);
829#endif
830
831    for (y = 0; y < height - 1; y += 2) {
832#if defined(HAS_RGB24TOYROW_NEON)
833      RGB24ToUVRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width);
834      RGB24ToYRow(src_rgb24, dst_y, width);
835      RGB24ToYRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width);
836#else
837      RGB24ToARGBRow(src_rgb24, row, width);
838      RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kRowSize, width);
839      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
840      ARGBToYRow(row, dst_y, width);
841      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
842#endif
843      src_rgb24 += src_stride_rgb24 * 2;
844      dst_y += dst_stride_y * 2;
845      dst_u += dst_stride_u;
846      dst_v += dst_stride_v;
847    }
848    if (height & 1) {
849#if defined(HAS_RGB24TOYROW_NEON)
850      RGB24ToUVRow(src_rgb24, 0, dst_u, dst_v, width);
851      RGB24ToYRow(src_rgb24, dst_y, width);
852#else
853      RGB24ToARGBRow(src_rgb24, row, width);
854      ARGBToUVRow(row, 0, dst_u, dst_v, width);
855      ARGBToYRow(row, dst_y, width);
856#endif
857    }
858#if !defined(HAS_RGB24TOYROW_NEON)
859    free_aligned_buffer_64(row);
860  }
861#endif
862  return 0;
863}
864
865// Convert RAW to I420.
866LIBYUV_API
867int RAWToI420(const uint8* src_raw, int src_stride_raw,
868              uint8* dst_y, int dst_stride_y,
869              uint8* dst_u, int dst_stride_u,
870              uint8* dst_v, int dst_stride_v,
871              int width, int height) {
872  int y;
873#if defined(HAS_RAWTOYROW_NEON)
874  void (*RAWToUVRow)(const uint8* src_raw, int src_stride_raw,
875      uint8* dst_u, uint8* dst_v, int width) = RAWToUVRow_C;
876  void (*RAWToYRow)(const uint8* src_raw, uint8* dst_y, int width) =
877      RAWToYRow_C;
878#else
879  void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
880      RAWToARGBRow_C;
881  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
882      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
883  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
884      ARGBToYRow_C;
885#endif
886  if (!src_raw || !dst_y || !dst_u || !dst_v ||
887      width <= 0 || height == 0) {
888    return -1;
889  }
890  // Negative height means invert the image.
891  if (height < 0) {
892    height = -height;
893    src_raw = src_raw + (height - 1) * src_stride_raw;
894    src_stride_raw = -src_stride_raw;
895  }
896
897// Neon version does direct RAW to YUV.
898#if defined(HAS_RAWTOYROW_NEON)
899  if (TestCpuFlag(kCpuHasNEON)) {
900    RAWToUVRow = RAWToUVRow_Any_NEON;
901    RAWToYRow = RAWToYRow_Any_NEON;
902    if (IS_ALIGNED(width, 8)) {
903      RAWToYRow = RAWToYRow_NEON;
904      if (IS_ALIGNED(width, 16)) {
905        RAWToUVRow = RAWToUVRow_NEON;
906      }
907    }
908  }
909// Other platforms do intermediate conversion from RAW to ARGB.
910#else
911#if defined(HAS_RAWTOARGBROW_SSSE3)
912  if (TestCpuFlag(kCpuHasSSSE3)) {
913    RAWToARGBRow = RAWToARGBRow_Any_SSSE3;
914    if (IS_ALIGNED(width, 16)) {
915      RAWToARGBRow = RAWToARGBRow_SSSE3;
916    }
917  }
918#endif
919#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
920  if (TestCpuFlag(kCpuHasSSSE3)) {
921    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
922    ARGBToYRow = ARGBToYRow_Any_SSSE3;
923    if (IS_ALIGNED(width, 16)) {
924      ARGBToUVRow = ARGBToUVRow_SSSE3;
925      ARGBToYRow = ARGBToYRow_SSSE3;
926    }
927  }
928#endif
929#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
930  if (TestCpuFlag(kCpuHasAVX2)) {
931    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
932    ARGBToYRow = ARGBToYRow_Any_AVX2;
933    if (IS_ALIGNED(width, 32)) {
934      ARGBToUVRow = ARGBToUVRow_AVX2;
935      ARGBToYRow = ARGBToYRow_AVX2;
936    }
937  }
938#endif
939  {
940    // Allocate 2 rows of ARGB.
941    const int kRowSize = (width * 4 + 31) & ~31;
942    align_buffer_64(row, kRowSize * 2);
943#endif
944
945    for (y = 0; y < height - 1; y += 2) {
946#if defined(HAS_RAWTOYROW_NEON)
947      RAWToUVRow(src_raw, src_stride_raw, dst_u, dst_v, width);
948      RAWToYRow(src_raw, dst_y, width);
949      RAWToYRow(src_raw + src_stride_raw, dst_y + dst_stride_y, width);
950#else
951      RAWToARGBRow(src_raw, row, width);
952      RAWToARGBRow(src_raw + src_stride_raw, row + kRowSize, width);
953      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
954      ARGBToYRow(row, dst_y, width);
955      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
956#endif
957      src_raw += src_stride_raw * 2;
958      dst_y += dst_stride_y * 2;
959      dst_u += dst_stride_u;
960      dst_v += dst_stride_v;
961    }
962    if (height & 1) {
963#if defined(HAS_RAWTOYROW_NEON)
964      RAWToUVRow(src_raw, 0, dst_u, dst_v, width);
965      RAWToYRow(src_raw, dst_y, width);
966#else
967      RAWToARGBRow(src_raw, row, width);
968      ARGBToUVRow(row, 0, dst_u, dst_v, width);
969      ARGBToYRow(row, dst_y, width);
970#endif
971    }
972#if !defined(HAS_RAWTOYROW_NEON)
973    free_aligned_buffer_64(row);
974  }
975#endif
976  return 0;
977}
978
979// Convert RGB565 to I420.
980LIBYUV_API
981int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565,
982                 uint8* dst_y, int dst_stride_y,
983                 uint8* dst_u, int dst_stride_u,
984                 uint8* dst_v, int dst_stride_v,
985                 int width, int height) {
986  int y;
987#if defined(HAS_RGB565TOYROW_NEON)
988  void (*RGB565ToUVRow)(const uint8* src_rgb565, int src_stride_rgb565,
989      uint8* dst_u, uint8* dst_v, int width) = RGB565ToUVRow_C;
990  void (*RGB565ToYRow)(const uint8* src_rgb565, uint8* dst_y, int width) =
991      RGB565ToYRow_C;
992#else
993  void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
994      RGB565ToARGBRow_C;
995  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
996      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
997  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
998      ARGBToYRow_C;
999#endif
1000  if (!src_rgb565 || !dst_y || !dst_u || !dst_v ||
1001      width <= 0 || height == 0) {
1002    return -1;
1003  }
1004  // Negative height means invert the image.
1005  if (height < 0) {
1006    height = -height;
1007    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
1008    src_stride_rgb565 = -src_stride_rgb565;
1009  }
1010
1011// Neon version does direct RGB565 to YUV.
1012#if defined(HAS_RGB565TOYROW_NEON)
1013  if (TestCpuFlag(kCpuHasNEON)) {
1014    RGB565ToUVRow = RGB565ToUVRow_Any_NEON;
1015    RGB565ToYRow = RGB565ToYRow_Any_NEON;
1016    if (IS_ALIGNED(width, 8)) {
1017      RGB565ToYRow = RGB565ToYRow_NEON;
1018      if (IS_ALIGNED(width, 16)) {
1019        RGB565ToUVRow = RGB565ToUVRow_NEON;
1020      }
1021    }
1022  }
1023// Other platforms do intermediate conversion from RGB565 to ARGB.
1024#else
1025#if defined(HAS_RGB565TOARGBROW_SSE2)
1026  if (TestCpuFlag(kCpuHasSSE2)) {
1027    RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
1028    if (IS_ALIGNED(width, 8)) {
1029      RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
1030    }
1031  }
1032#endif
1033#if defined(HAS_RGB565TOARGBROW_AVX2)
1034  if (TestCpuFlag(kCpuHasAVX2)) {
1035    RGB565ToARGBRow = RGB565ToARGBRow_Any_AVX2;
1036    if (IS_ALIGNED(width, 16)) {
1037      RGB565ToARGBRow = RGB565ToARGBRow_AVX2;
1038    }
1039  }
1040#endif
1041#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
1042  if (TestCpuFlag(kCpuHasSSSE3)) {
1043    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1044    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1045    if (IS_ALIGNED(width, 16)) {
1046      ARGBToUVRow = ARGBToUVRow_SSSE3;
1047      ARGBToYRow = ARGBToYRow_SSSE3;
1048    }
1049  }
1050#endif
1051#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
1052  if (TestCpuFlag(kCpuHasAVX2)) {
1053    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
1054    ARGBToYRow = ARGBToYRow_Any_AVX2;
1055    if (IS_ALIGNED(width, 32)) {
1056      ARGBToUVRow = ARGBToUVRow_AVX2;
1057      ARGBToYRow = ARGBToYRow_AVX2;
1058    }
1059  }
1060#endif
1061  {
1062    // Allocate 2 rows of ARGB.
1063    const int kRowSize = (width * 4 + 31) & ~31;
1064    align_buffer_64(row, kRowSize * 2);
1065#endif
1066
1067    for (y = 0; y < height - 1; y += 2) {
1068#if defined(HAS_RGB565TOYROW_NEON)
1069      RGB565ToUVRow(src_rgb565, src_stride_rgb565, dst_u, dst_v, width);
1070      RGB565ToYRow(src_rgb565, dst_y, width);
1071      RGB565ToYRow(src_rgb565 + src_stride_rgb565, dst_y + dst_stride_y, width);
1072#else
1073      RGB565ToARGBRow(src_rgb565, row, width);
1074      RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kRowSize, width);
1075      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
1076      ARGBToYRow(row, dst_y, width);
1077      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
1078#endif
1079      src_rgb565 += src_stride_rgb565 * 2;
1080      dst_y += dst_stride_y * 2;
1081      dst_u += dst_stride_u;
1082      dst_v += dst_stride_v;
1083    }
1084    if (height & 1) {
1085#if defined(HAS_RGB565TOYROW_NEON)
1086      RGB565ToUVRow(src_rgb565, 0, dst_u, dst_v, width);
1087      RGB565ToYRow(src_rgb565, dst_y, width);
1088#else
1089      RGB565ToARGBRow(src_rgb565, row, width);
1090      ARGBToUVRow(row, 0, dst_u, dst_v, width);
1091      ARGBToYRow(row, dst_y, width);
1092#endif
1093    }
1094#if !defined(HAS_RGB565TOYROW_NEON)
1095    free_aligned_buffer_64(row);
1096  }
1097#endif
1098  return 0;
1099}
1100
1101// Convert ARGB1555 to I420.
1102LIBYUV_API
1103int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555,
1104                   uint8* dst_y, int dst_stride_y,
1105                   uint8* dst_u, int dst_stride_u,
1106                   uint8* dst_v, int dst_stride_v,
1107                   int width, int height) {
1108  int y;
1109#if defined(HAS_ARGB1555TOYROW_NEON)
1110  void (*ARGB1555ToUVRow)(const uint8* src_argb1555, int src_stride_argb1555,
1111      uint8* dst_u, uint8* dst_v, int width) = ARGB1555ToUVRow_C;
1112  void (*ARGB1555ToYRow)(const uint8* src_argb1555, uint8* dst_y, int width) =
1113      ARGB1555ToYRow_C;
1114#else
1115  void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
1116      ARGB1555ToARGBRow_C;
1117  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1118      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
1119  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
1120      ARGBToYRow_C;
1121#endif
1122  if (!src_argb1555 || !dst_y || !dst_u || !dst_v ||
1123      width <= 0 || height == 0) {
1124    return -1;
1125  }
1126  // Negative height means invert the image.
1127  if (height < 0) {
1128    height = -height;
1129    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
1130    src_stride_argb1555 = -src_stride_argb1555;
1131  }
1132
1133// Neon version does direct ARGB1555 to YUV.
1134#if defined(HAS_ARGB1555TOYROW_NEON)
1135  if (TestCpuFlag(kCpuHasNEON)) {
1136    ARGB1555ToUVRow = ARGB1555ToUVRow_Any_NEON;
1137    ARGB1555ToYRow = ARGB1555ToYRow_Any_NEON;
1138    if (IS_ALIGNED(width, 8)) {
1139      ARGB1555ToYRow = ARGB1555ToYRow_NEON;
1140      if (IS_ALIGNED(width, 16)) {
1141        ARGB1555ToUVRow = ARGB1555ToUVRow_NEON;
1142      }
1143    }
1144  }
1145// Other platforms do intermediate conversion from ARGB1555 to ARGB.
1146#else
1147#if defined(HAS_ARGB1555TOARGBROW_SSE2)
1148  if (TestCpuFlag(kCpuHasSSE2)) {
1149    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_SSE2;
1150    if (IS_ALIGNED(width, 8)) {
1151      ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
1152    }
1153  }
1154#endif
1155#if defined(HAS_ARGB1555TOARGBROW_AVX2)
1156  if (TestCpuFlag(kCpuHasAVX2)) {
1157    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_AVX2;
1158    if (IS_ALIGNED(width, 16)) {
1159      ARGB1555ToARGBRow = ARGB1555ToARGBRow_AVX2;
1160    }
1161  }
1162#endif
1163#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
1164  if (TestCpuFlag(kCpuHasSSSE3)) {
1165    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1166    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1167    if (IS_ALIGNED(width, 16)) {
1168      ARGBToUVRow = ARGBToUVRow_SSSE3;
1169      ARGBToYRow = ARGBToYRow_SSSE3;
1170    }
1171  }
1172#endif
1173#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
1174  if (TestCpuFlag(kCpuHasAVX2)) {
1175    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
1176    ARGBToYRow = ARGBToYRow_Any_AVX2;
1177    if (IS_ALIGNED(width, 32)) {
1178      ARGBToUVRow = ARGBToUVRow_AVX2;
1179      ARGBToYRow = ARGBToYRow_AVX2;
1180    }
1181  }
1182#endif
1183  {
1184    // Allocate 2 rows of ARGB.
1185    const int kRowSize = (width * 4 + 31) & ~31;
1186    align_buffer_64(row, kRowSize * 2);
1187#endif
1188
1189    for (y = 0; y < height - 1; y += 2) {
1190#if defined(HAS_ARGB1555TOYROW_NEON)
1191      ARGB1555ToUVRow(src_argb1555, src_stride_argb1555, dst_u, dst_v, width);
1192      ARGB1555ToYRow(src_argb1555, dst_y, width);
1193      ARGB1555ToYRow(src_argb1555 + src_stride_argb1555, dst_y + dst_stride_y,
1194                     width);
1195#else
1196      ARGB1555ToARGBRow(src_argb1555, row, width);
1197      ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555, row + kRowSize,
1198                        width);
1199      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
1200      ARGBToYRow(row, dst_y, width);
1201      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
1202#endif
1203      src_argb1555 += src_stride_argb1555 * 2;
1204      dst_y += dst_stride_y * 2;
1205      dst_u += dst_stride_u;
1206      dst_v += dst_stride_v;
1207    }
1208    if (height & 1) {
1209#if defined(HAS_ARGB1555TOYROW_NEON)
1210      ARGB1555ToUVRow(src_argb1555, 0, dst_u, dst_v, width);
1211      ARGB1555ToYRow(src_argb1555, dst_y, width);
1212#else
1213      ARGB1555ToARGBRow(src_argb1555, row, width);
1214      ARGBToUVRow(row, 0, dst_u, dst_v, width);
1215      ARGBToYRow(row, dst_y, width);
1216#endif
1217    }
1218#if !defined(HAS_ARGB1555TOYROW_NEON)
1219    free_aligned_buffer_64(row);
1220  }
1221#endif
1222  return 0;
1223}
1224
1225// Convert ARGB4444 to I420.
1226LIBYUV_API
1227int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444,
1228                   uint8* dst_y, int dst_stride_y,
1229                   uint8* dst_u, int dst_stride_u,
1230                   uint8* dst_v, int dst_stride_v,
1231                   int width, int height) {
1232  int y;
1233#if defined(HAS_ARGB4444TOYROW_NEON)
1234  void (*ARGB4444ToUVRow)(const uint8* src_argb4444, int src_stride_argb4444,
1235      uint8* dst_u, uint8* dst_v, int width) = ARGB4444ToUVRow_C;
1236  void (*ARGB4444ToYRow)(const uint8* src_argb4444, uint8* dst_y, int width) =
1237      ARGB4444ToYRow_C;
1238#else
1239  void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
1240      ARGB4444ToARGBRow_C;
1241  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1242      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
1243  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
1244      ARGBToYRow_C;
1245#endif
1246  if (!src_argb4444 || !dst_y || !dst_u || !dst_v ||
1247      width <= 0 || height == 0) {
1248    return -1;
1249  }
1250  // Negative height means invert the image.
1251  if (height < 0) {
1252    height = -height;
1253    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
1254    src_stride_argb4444 = -src_stride_argb4444;
1255  }
1256
1257// Neon version does direct ARGB4444 to YUV.
1258#if defined(HAS_ARGB4444TOYROW_NEON)
1259  if (TestCpuFlag(kCpuHasNEON)) {
1260    ARGB4444ToUVRow = ARGB4444ToUVRow_Any_NEON;
1261    ARGB4444ToYRow = ARGB4444ToYRow_Any_NEON;
1262    if (IS_ALIGNED(width, 8)) {
1263      ARGB4444ToYRow = ARGB4444ToYRow_NEON;
1264      if (IS_ALIGNED(width, 16)) {
1265        ARGB4444ToUVRow = ARGB4444ToUVRow_NEON;
1266      }
1267    }
1268  }
1269// Other platforms do intermediate conversion from ARGB4444 to ARGB.
1270#else
1271#if defined(HAS_ARGB4444TOARGBROW_SSE2)
1272  if (TestCpuFlag(kCpuHasSSE2)) {
1273    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_SSE2;
1274    if (IS_ALIGNED(width, 8)) {
1275      ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
1276    }
1277  }
1278#endif
1279#if defined(HAS_ARGB4444TOARGBROW_AVX2)
1280  if (TestCpuFlag(kCpuHasAVX2)) {
1281    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_AVX2;
1282    if (IS_ALIGNED(width, 16)) {
1283      ARGB4444ToARGBRow = ARGB4444ToARGBRow_AVX2;
1284    }
1285  }
1286#endif
1287#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
1288  if (TestCpuFlag(kCpuHasSSSE3)) {
1289    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1290    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1291    if (IS_ALIGNED(width, 16)) {
1292      ARGBToUVRow = ARGBToUVRow_SSSE3;
1293      ARGBToYRow = ARGBToYRow_SSSE3;
1294    }
1295  }
1296#endif
1297#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
1298  if (TestCpuFlag(kCpuHasAVX2)) {
1299    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
1300    ARGBToYRow = ARGBToYRow_Any_AVX2;
1301    if (IS_ALIGNED(width, 32)) {
1302      ARGBToUVRow = ARGBToUVRow_AVX2;
1303      ARGBToYRow = ARGBToYRow_AVX2;
1304    }
1305  }
1306#endif
1307  {
1308    // Allocate 2 rows of ARGB.
1309    const int kRowSize = (width * 4 + 31) & ~31;
1310    align_buffer_64(row, kRowSize * 2);
1311#endif
1312
1313    for (y = 0; y < height - 1; y += 2) {
1314#if defined(HAS_ARGB4444TOYROW_NEON)
1315      ARGB4444ToUVRow(src_argb4444, src_stride_argb4444, dst_u, dst_v, width);
1316      ARGB4444ToYRow(src_argb4444, dst_y, width);
1317      ARGB4444ToYRow(src_argb4444 + src_stride_argb4444, dst_y + dst_stride_y,
1318                     width);
1319#else
1320      ARGB4444ToARGBRow(src_argb4444, row, width);
1321      ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444, row + kRowSize,
1322                        width);
1323      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
1324      ARGBToYRow(row, dst_y, width);
1325      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
1326#endif
1327      src_argb4444 += src_stride_argb4444 * 2;
1328      dst_y += dst_stride_y * 2;
1329      dst_u += dst_stride_u;
1330      dst_v += dst_stride_v;
1331    }
1332    if (height & 1) {
1333#if defined(HAS_ARGB4444TOYROW_NEON)
1334      ARGB4444ToUVRow(src_argb4444, 0, dst_u, dst_v, width);
1335      ARGB4444ToYRow(src_argb4444, dst_y, width);
1336#else
1337      ARGB4444ToARGBRow(src_argb4444, row, width);
1338      ARGBToUVRow(row, 0, dst_u, dst_v, width);
1339      ARGBToYRow(row, dst_y, width);
1340#endif
1341    }
1342#if !defined(HAS_ARGB4444TOYROW_NEON)
1343    free_aligned_buffer_64(row);
1344  }
1345#endif
1346  return 0;
1347}
1348
1349static void SplitPixels(const uint8* src_u, int src_pixel_stride_uv,
1350                        uint8* dst_u, int width) {
1351  int i;
1352  for (i = 0; i < width; ++i) {
1353    *dst_u = *src_u;
1354    ++dst_u;
1355    src_u += src_pixel_stride_uv;
1356  }
1357}
1358
1359// Convert Android420 to I420.
1360LIBYUV_API
1361int Android420ToI420(const uint8* src_y, int src_stride_y,
1362                     const uint8* src_u, int src_stride_u,
1363                     const uint8* src_v, int src_stride_v,
1364                     int src_pixel_stride_uv,
1365                     uint8* dst_y, int dst_stride_y,
1366                     uint8* dst_u, int dst_stride_u,
1367                     uint8* dst_v, int dst_stride_v,
1368                     int width, int height) {
1369  int y;
1370  const int vu_off = src_v - src_u;
1371  int halfwidth = (width + 1) >> 1;
1372  int halfheight = (height + 1) >> 1;
1373  if (!src_u || !src_v ||
1374      !dst_u || !dst_v ||
1375      width <= 0 || height == 0) {
1376    return -1;
1377  }
1378  // Negative height means invert the image.
1379  if (height < 0) {
1380    height = -height;
1381    halfheight = (height + 1) >> 1;
1382    src_y = src_y + (height - 1) * src_stride_y;
1383    src_u = src_u + (halfheight - 1) * src_stride_u;
1384    src_v = src_v + (halfheight - 1) * src_stride_v;
1385    src_stride_y = -src_stride_y;
1386    src_stride_u = -src_stride_u;
1387    src_stride_v = -src_stride_v;
1388  }
1389
1390  if (dst_y) {
1391    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1392  }
1393
1394  // Copy UV planes as is - I420
1395  if (src_pixel_stride_uv == 1) {
1396    CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
1397    CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
1398    return 0;
1399  // Split UV planes - NV21
1400  } else if (src_pixel_stride_uv == 2 && vu_off == -1 &&
1401             src_stride_u == src_stride_v) {
1402    SplitUVPlane(src_v, src_stride_v, dst_v, dst_stride_v, dst_u, dst_stride_u,
1403                 halfwidth, halfheight);
1404    return 0;
1405  // Split UV planes - NV12
1406  } else if (src_pixel_stride_uv == 2 && vu_off == 1 &&
1407             src_stride_u == src_stride_v) {
1408    SplitUVPlane(src_u, src_stride_u, dst_u, dst_stride_u, dst_v, dst_stride_v,
1409                 halfwidth, halfheight);
1410    return 0;
1411  }
1412
1413  for (y = 0; y < halfheight; ++y) {
1414    SplitPixels(src_u, src_pixel_stride_uv, dst_u, halfwidth);
1415    SplitPixels(src_v, src_pixel_stride_uv, dst_v, halfwidth);
1416    src_u += src_stride_u;
1417    src_v += src_stride_v;
1418    dst_u += dst_stride_u;
1419    dst_v += dst_stride_v;
1420  }
1421  return 0;
1422}
1423
1424#ifdef __cplusplus
1425}  // extern "C"
1426}  // namespace libyuv
1427#endif
1428