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