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