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