1 /*
2  *  Copyright 2012 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_from.h"
12 
13 #include "libyuv/basic_types.h"
14 #include "libyuv/convert.h"  // For I420Copy
15 #include "libyuv/cpu_id.h"
16 #include "libyuv/planar_functions.h"
17 #include "libyuv/rotate.h"
18 #include "libyuv/scale.h"  // For ScalePlane()
19 #include "libyuv/video_common.h"
20 #include "libyuv/row.h"
21 
22 #ifdef __cplusplus
23 namespace libyuv {
24 extern "C" {
25 #endif
26 
27 #define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
28 static __inline int Abs(int v) {
29   return v >= 0 ? v : -v;
30 }
31 
32 // I420 To any I4xx YUV format with mirroring.
33 static int I420ToI4xx(const uint8* src_y, int src_stride_y,
34                       const uint8* src_u, int src_stride_u,
35                       const uint8* src_v, int src_stride_v,
36                       uint8* dst_y, int dst_stride_y,
37                       uint8* dst_u, int dst_stride_u,
38                       uint8* dst_v, int dst_stride_v,
39                       int src_y_width, int src_y_height,
40                       int dst_uv_width, int dst_uv_height) {
41   const int dst_y_width = Abs(src_y_width);
42   const int dst_y_height = Abs(src_y_height);
43   const int src_uv_width = SUBSAMPLE(src_y_width, 1, 1);
44   const int src_uv_height = SUBSAMPLE(src_y_height, 1, 1);
45   if (src_y_width == 0 || src_y_height == 0 ||
46       dst_uv_width <= 0 || dst_uv_height <= 0) {
47     return -1;
48   }
49   ScalePlane(src_y, src_stride_y, src_y_width, src_y_height,
50              dst_y, dst_stride_y, dst_y_width, dst_y_height,
51              kFilterBilinear);
52   ScalePlane(src_u, src_stride_u, src_uv_width, src_uv_height,
53              dst_u, dst_stride_u, dst_uv_width, dst_uv_height,
54              kFilterBilinear);
55   ScalePlane(src_v, src_stride_v, src_uv_width, src_uv_height,
56              dst_v, dst_stride_v, dst_uv_width, dst_uv_height,
57              kFilterBilinear);
58   return 0;
59 }
60 
61 // 420 chroma is 1/2 width, 1/2 height
62 // 422 chroma is 1/2 width, 1x height
63 LIBYUV_API
64 int I420ToI422(const uint8* src_y, int src_stride_y,
65                const uint8* src_u, int src_stride_u,
66                const uint8* src_v, int src_stride_v,
67                uint8* dst_y, int dst_stride_y,
68                uint8* dst_u, int dst_stride_u,
69                uint8* dst_v, int dst_stride_v,
70                int width, int height) {
71   const int dst_uv_width = (Abs(width) + 1) >> 1;
72   const int dst_uv_height = Abs(height);
73   return I420ToI4xx(src_y, src_stride_y,
74                     src_u, src_stride_u,
75                     src_v, src_stride_v,
76                     dst_y, dst_stride_y,
77                     dst_u, dst_stride_u,
78                     dst_v, dst_stride_v,
79                     width, height,
80                     dst_uv_width, dst_uv_height);
81 }
82 
83 // 420 chroma is 1/2 width, 1/2 height
84 // 444 chroma is 1x width, 1x height
85 LIBYUV_API
86 int I420ToI444(const uint8* src_y, int src_stride_y,
87                const uint8* src_u, int src_stride_u,
88                const uint8* src_v, int src_stride_v,
89                uint8* dst_y, int dst_stride_y,
90                uint8* dst_u, int dst_stride_u,
91                uint8* dst_v, int dst_stride_v,
92                int width, int height) {
93   const int dst_uv_width = Abs(width);
94   const int dst_uv_height = Abs(height);
95   return I420ToI4xx(src_y, src_stride_y,
96                     src_u, src_stride_u,
97                     src_v, src_stride_v,
98                     dst_y, dst_stride_y,
99                     dst_u, dst_stride_u,
100                     dst_v, dst_stride_v,
101                     width, height,
102                     dst_uv_width, dst_uv_height);
103 }
104 
105 // 420 chroma is 1/2 width, 1/2 height
106 // 411 chroma is 1/4 width, 1x height
107 LIBYUV_API
108 int I420ToI411(const uint8* src_y, int src_stride_y,
109                const uint8* src_u, int src_stride_u,
110                const uint8* src_v, int src_stride_v,
111                uint8* dst_y, int dst_stride_y,
112                uint8* dst_u, int dst_stride_u,
113                uint8* dst_v, int dst_stride_v,
114                int width, int height) {
115   const int dst_uv_width = (Abs(width) + 3) >> 2;
116   const int dst_uv_height = Abs(height);
117   return I420ToI4xx(src_y, src_stride_y,
118                     src_u, src_stride_u,
119                     src_v, src_stride_v,
120                     dst_y, dst_stride_y,
121                     dst_u, dst_stride_u,
122                     dst_v, dst_stride_v,
123                     width, height,
124                     dst_uv_width, dst_uv_height);
125 }
126 
127 // Copy to I400. Source can be I420,422,444,400,NV12,NV21
128 LIBYUV_API
129 int I400Copy(const uint8* src_y, int src_stride_y,
130              uint8* dst_y, int dst_stride_y,
131              int width, int height) {
132   if (!src_y || !dst_y ||
133       width <= 0 || height == 0) {
134     return -1;
135   }
136   // Negative height means invert the image.
137   if (height < 0) {
138     height = -height;
139     src_y = src_y + (height - 1) * src_stride_y;
140     src_stride_y = -src_stride_y;
141   }
142   CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
143   return 0;
144 }
145 
146 LIBYUV_API
147 int I422ToYUY2(const uint8* src_y, int src_stride_y,
148                const uint8* src_u, int src_stride_u,
149                const uint8* src_v, int src_stride_v,
150                uint8* dst_yuy2, int dst_stride_yuy2,
151                int width, int height) {
152   int y;
153   void (*I422ToYUY2Row)(const uint8* src_y, const uint8* src_u,
154                         const uint8* src_v, uint8* dst_yuy2, int width) =
155       I422ToYUY2Row_C;
156   if (!src_y || !src_u || !src_v || !dst_yuy2 ||
157       width <= 0 || height == 0) {
158     return -1;
159   }
160   // Negative height means invert the image.
161   if (height < 0) {
162     height = -height;
163     dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2;
164     dst_stride_yuy2 = -dst_stride_yuy2;
165   }
166   // Coalesce rows.
167   if (src_stride_y == width &&
168       src_stride_u * 2 == width &&
169       src_stride_v * 2 == width &&
170       dst_stride_yuy2 == width * 2) {
171     width *= height;
172     height = 1;
173     src_stride_y = src_stride_u = src_stride_v = dst_stride_yuy2 = 0;
174   }
175 #if defined(HAS_I422TOYUY2ROW_SSE2)
176   if (TestCpuFlag(kCpuHasSSE2)) {
177     I422ToYUY2Row = I422ToYUY2Row_Any_SSE2;
178     if (IS_ALIGNED(width, 16)) {
179       I422ToYUY2Row = I422ToYUY2Row_SSE2;
180     }
181   }
182 #endif
183 #if defined(HAS_I422TOYUY2ROW_NEON)
184   if (TestCpuFlag(kCpuHasNEON)) {
185     I422ToYUY2Row = I422ToYUY2Row_Any_NEON;
186     if (IS_ALIGNED(width, 16)) {
187       I422ToYUY2Row = I422ToYUY2Row_NEON;
188     }
189   }
190 #endif
191 
192   for (y = 0; y < height; ++y) {
193     I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width);
194     src_y += src_stride_y;
195     src_u += src_stride_u;
196     src_v += src_stride_v;
197     dst_yuy2 += dst_stride_yuy2;
198   }
199   return 0;
200 }
201 
202 LIBYUV_API
203 int I420ToYUY2(const uint8* src_y, int src_stride_y,
204                const uint8* src_u, int src_stride_u,
205                const uint8* src_v, int src_stride_v,
206                uint8* dst_yuy2, int dst_stride_yuy2,
207                int width, int height) {
208   int y;
209   void (*I422ToYUY2Row)(const uint8* src_y, const uint8* src_u,
210                         const uint8* src_v, uint8* dst_yuy2, int width) =
211       I422ToYUY2Row_C;
212   if (!src_y || !src_u || !src_v || !dst_yuy2 ||
213       width <= 0 || height == 0) {
214     return -1;
215   }
216   // Negative height means invert the image.
217   if (height < 0) {
218     height = -height;
219     dst_yuy2 = dst_yuy2 + (height - 1) * dst_stride_yuy2;
220     dst_stride_yuy2 = -dst_stride_yuy2;
221   }
222 #if defined(HAS_I422TOYUY2ROW_SSE2)
223   if (TestCpuFlag(kCpuHasSSE2)) {
224     I422ToYUY2Row = I422ToYUY2Row_Any_SSE2;
225     if (IS_ALIGNED(width, 16)) {
226       I422ToYUY2Row = I422ToYUY2Row_SSE2;
227     }
228   }
229 #endif
230 #if defined(HAS_I422TOYUY2ROW_NEON)
231   if (TestCpuFlag(kCpuHasNEON)) {
232     I422ToYUY2Row = I422ToYUY2Row_Any_NEON;
233     if (IS_ALIGNED(width, 16)) {
234       I422ToYUY2Row = I422ToYUY2Row_NEON;
235     }
236   }
237 #endif
238 
239   for (y = 0; y < height - 1; y += 2) {
240     I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width);
241     I422ToYUY2Row(src_y + src_stride_y, src_u, src_v,
242                   dst_yuy2 + dst_stride_yuy2, width);
243     src_y += src_stride_y * 2;
244     src_u += src_stride_u;
245     src_v += src_stride_v;
246     dst_yuy2 += dst_stride_yuy2 * 2;
247   }
248   if (height & 1) {
249     I422ToYUY2Row(src_y, src_u, src_v, dst_yuy2, width);
250   }
251   return 0;
252 }
253 
254 LIBYUV_API
255 int I422ToUYVY(const uint8* src_y, int src_stride_y,
256                const uint8* src_u, int src_stride_u,
257                const uint8* src_v, int src_stride_v,
258                uint8* dst_uyvy, int dst_stride_uyvy,
259                int width, int height) {
260   int y;
261   void (*I422ToUYVYRow)(const uint8* src_y, const uint8* src_u,
262                         const uint8* src_v, uint8* dst_uyvy, int width) =
263       I422ToUYVYRow_C;
264   if (!src_y || !src_u || !src_v || !dst_uyvy ||
265       width <= 0 || height == 0) {
266     return -1;
267   }
268   // Negative height means invert the image.
269   if (height < 0) {
270     height = -height;
271     dst_uyvy = dst_uyvy + (height - 1) * dst_stride_uyvy;
272     dst_stride_uyvy = -dst_stride_uyvy;
273   }
274   // Coalesce rows.
275   if (src_stride_y == width &&
276       src_stride_u * 2 == width &&
277       src_stride_v * 2 == width &&
278       dst_stride_uyvy == width * 2) {
279     width *= height;
280     height = 1;
281     src_stride_y = src_stride_u = src_stride_v = dst_stride_uyvy = 0;
282   }
283 #if defined(HAS_I422TOUYVYROW_SSE2)
284   if (TestCpuFlag(kCpuHasSSE2)) {
285     I422ToUYVYRow = I422ToUYVYRow_Any_SSE2;
286     if (IS_ALIGNED(width, 16)) {
287       I422ToUYVYRow = I422ToUYVYRow_SSE2;
288     }
289   }
290 #endif
291 #if defined(HAS_I422TOUYVYROW_NEON)
292   if (TestCpuFlag(kCpuHasNEON)) {
293     I422ToUYVYRow = I422ToUYVYRow_Any_NEON;
294     if (IS_ALIGNED(width, 16)) {
295       I422ToUYVYRow = I422ToUYVYRow_NEON;
296     }
297   }
298 #endif
299 
300   for (y = 0; y < height; ++y) {
301     I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width);
302     src_y += src_stride_y;
303     src_u += src_stride_u;
304     src_v += src_stride_v;
305     dst_uyvy += dst_stride_uyvy;
306   }
307   return 0;
308 }
309 
310 LIBYUV_API
311 int I420ToUYVY(const uint8* src_y, int src_stride_y,
312                const uint8* src_u, int src_stride_u,
313                const uint8* src_v, int src_stride_v,
314                uint8* dst_uyvy, int dst_stride_uyvy,
315                int width, int height) {
316   int y;
317   void (*I422ToUYVYRow)(const uint8* src_y, const uint8* src_u,
318                         const uint8* src_v, uint8* dst_uyvy, int width) =
319       I422ToUYVYRow_C;
320   if (!src_y || !src_u || !src_v || !dst_uyvy ||
321       width <= 0 || height == 0) {
322     return -1;
323   }
324   // Negative height means invert the image.
325   if (height < 0) {
326     height = -height;
327     dst_uyvy = dst_uyvy + (height - 1) * dst_stride_uyvy;
328     dst_stride_uyvy = -dst_stride_uyvy;
329   }
330 #if defined(HAS_I422TOUYVYROW_SSE2)
331   if (TestCpuFlag(kCpuHasSSE2)) {
332     I422ToUYVYRow = I422ToUYVYRow_Any_SSE2;
333     if (IS_ALIGNED(width, 16)) {
334       I422ToUYVYRow = I422ToUYVYRow_SSE2;
335     }
336   }
337 #endif
338 #if defined(HAS_I422TOUYVYROW_NEON)
339   if (TestCpuFlag(kCpuHasNEON)) {
340     I422ToUYVYRow = I422ToUYVYRow_Any_NEON;
341     if (IS_ALIGNED(width, 16)) {
342       I422ToUYVYRow = I422ToUYVYRow_NEON;
343     }
344   }
345 #endif
346 
347   for (y = 0; y < height - 1; y += 2) {
348     I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width);
349     I422ToUYVYRow(src_y + src_stride_y, src_u, src_v,
350                   dst_uyvy + dst_stride_uyvy, width);
351     src_y += src_stride_y * 2;
352     src_u += src_stride_u;
353     src_v += src_stride_v;
354     dst_uyvy += dst_stride_uyvy * 2;
355   }
356   if (height & 1) {
357     I422ToUYVYRow(src_y, src_u, src_v, dst_uyvy, width);
358   }
359   return 0;
360 }
361 
362 LIBYUV_API
363 int I420ToNV12(const uint8* src_y, int src_stride_y,
364                const uint8* src_u, int src_stride_u,
365                const uint8* src_v, int src_stride_v,
366                uint8* dst_y, int dst_stride_y,
367                uint8* dst_uv, int dst_stride_uv,
368                int width, int height) {
369   int y;
370   void (*MergeUVRow_)(const uint8* src_u, const uint8* src_v, uint8* dst_uv,
371       int width) = MergeUVRow_C;
372   // Coalesce rows.
373   int halfwidth = (width + 1) >> 1;
374   int halfheight = (height + 1) >> 1;
375   if (!src_y || !src_u || !src_v || !dst_y || !dst_uv ||
376       width <= 0 || height == 0) {
377     return -1;
378   }
379   // Negative height means invert the image.
380   if (height < 0) {
381     height = -height;
382     halfheight = (height + 1) >> 1;
383     dst_y = dst_y + (height - 1) * dst_stride_y;
384     dst_uv = dst_uv + (halfheight - 1) * dst_stride_uv;
385     dst_stride_y = -dst_stride_y;
386     dst_stride_uv = -dst_stride_uv;
387   }
388   if (src_stride_y == width &&
389       dst_stride_y == width) {
390     width *= height;
391     height = 1;
392     src_stride_y = dst_stride_y = 0;
393   }
394   // Coalesce rows.
395   if (src_stride_u == halfwidth &&
396       src_stride_v == halfwidth &&
397       dst_stride_uv == halfwidth * 2) {
398     halfwidth *= halfheight;
399     halfheight = 1;
400     src_stride_u = src_stride_v = dst_stride_uv = 0;
401   }
402 #if defined(HAS_MERGEUVROW_SSE2)
403   if (TestCpuFlag(kCpuHasSSE2)) {
404     MergeUVRow_ = MergeUVRow_Any_SSE2;
405     if (IS_ALIGNED(halfwidth, 16)) {
406       MergeUVRow_ = MergeUVRow_SSE2;
407     }
408   }
409 #endif
410 #if defined(HAS_MERGEUVROW_AVX2)
411   if (TestCpuFlag(kCpuHasAVX2)) {
412     MergeUVRow_ = MergeUVRow_Any_AVX2;
413     if (IS_ALIGNED(halfwidth, 32)) {
414       MergeUVRow_ = MergeUVRow_AVX2;
415     }
416   }
417 #endif
418 #if defined(HAS_MERGEUVROW_NEON)
419   if (TestCpuFlag(kCpuHasNEON)) {
420     MergeUVRow_ = MergeUVRow_Any_NEON;
421     if (IS_ALIGNED(halfwidth, 16)) {
422       MergeUVRow_ = MergeUVRow_NEON;
423     }
424   }
425 #endif
426 
427   CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
428   for (y = 0; y < halfheight; ++y) {
429     // Merge a row of U and V into a row of UV.
430     MergeUVRow_(src_u, src_v, dst_uv, halfwidth);
431     src_u += src_stride_u;
432     src_v += src_stride_v;
433     dst_uv += dst_stride_uv;
434   }
435   return 0;
436 }
437 
438 LIBYUV_API
439 int I420ToNV21(const uint8* src_y, int src_stride_y,
440                const uint8* src_u, int src_stride_u,
441                const uint8* src_v, int src_stride_v,
442                uint8* dst_y, int dst_stride_y,
443                uint8* dst_vu, int dst_stride_vu,
444                int width, int height) {
445   return I420ToNV12(src_y, src_stride_y,
446                     src_v, src_stride_v,
447                     src_u, src_stride_u,
448                     dst_y, dst_stride_y,
449                     dst_vu, dst_stride_vu,
450                     width, height);
451 }
452 
453 // Convert I422 to RGBA with matrix
454 static int I420ToRGBAMatrix(const uint8* src_y, int src_stride_y,
455                             const uint8* src_u, int src_stride_u,
456                             const uint8* src_v, int src_stride_v,
457                             uint8* dst_rgba, int dst_stride_rgba,
458                             const struct YuvConstants* yuvconstants,
459                             int width, int height) {
460   int y;
461   void (*I422ToRGBARow)(const uint8* y_buf,
462                         const uint8* u_buf,
463                         const uint8* v_buf,
464                         uint8* rgb_buf,
465                         const struct YuvConstants* yuvconstants,
466                         int width) = I422ToRGBARow_C;
467   if (!src_y || !src_u || !src_v || !dst_rgba ||
468       width <= 0 || height == 0) {
469     return -1;
470   }
471   // Negative height means invert the image.
472   if (height < 0) {
473     height = -height;
474     dst_rgba = dst_rgba + (height - 1) * dst_stride_rgba;
475     dst_stride_rgba = -dst_stride_rgba;
476   }
477 #if defined(HAS_I422TORGBAROW_SSSE3)
478   if (TestCpuFlag(kCpuHasSSSE3)) {
479     I422ToRGBARow = I422ToRGBARow_Any_SSSE3;
480     if (IS_ALIGNED(width, 8)) {
481       I422ToRGBARow = I422ToRGBARow_SSSE3;
482     }
483   }
484 #endif
485 #if defined(HAS_I422TORGBAROW_AVX2)
486   if (TestCpuFlag(kCpuHasAVX2)) {
487     I422ToRGBARow = I422ToRGBARow_Any_AVX2;
488     if (IS_ALIGNED(width, 16)) {
489       I422ToRGBARow = I422ToRGBARow_AVX2;
490     }
491   }
492 #endif
493 #if defined(HAS_I422TORGBAROW_NEON)
494   if (TestCpuFlag(kCpuHasNEON)) {
495     I422ToRGBARow = I422ToRGBARow_Any_NEON;
496     if (IS_ALIGNED(width, 8)) {
497       I422ToRGBARow = I422ToRGBARow_NEON;
498     }
499   }
500 #endif
501 #if defined(HAS_I422TORGBAROW_DSPR2)
502   if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(width, 4) &&
503       IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) &&
504       IS_ALIGNED(src_u, 2) && IS_ALIGNED(src_stride_u, 2) &&
505       IS_ALIGNED(src_v, 2) && IS_ALIGNED(src_stride_v, 2) &&
506       IS_ALIGNED(dst_rgba, 4) && IS_ALIGNED(dst_stride_rgba, 4)) {
507     I422ToRGBARow = I422ToRGBARow_DSPR2;
508   }
509 #endif
510 
511   for (y = 0; y < height; ++y) {
512     I422ToRGBARow(src_y, src_u, src_v, dst_rgba, yuvconstants, width);
513     dst_rgba += dst_stride_rgba;
514     src_y += src_stride_y;
515     if (y & 1) {
516       src_u += src_stride_u;
517       src_v += src_stride_v;
518     }
519   }
520   return 0;
521 }
522 
523 // Convert I420 to RGBA.
524 LIBYUV_API
525 int I420ToRGBA(const uint8* src_y, int src_stride_y,
526                const uint8* src_u, int src_stride_u,
527                const uint8* src_v, int src_stride_v,
528                uint8* dst_rgba, int dst_stride_rgba,
529                int width, int height) {
530   return I420ToRGBAMatrix(src_y, src_stride_y,
531                           src_u, src_stride_u,
532                           src_v, src_stride_v,
533                           dst_rgba, dst_stride_rgba,
534                           &kYuvI601Constants,
535                           width, height);
536 }
537 
538 // Convert I420 to BGRA.
539 LIBYUV_API
540 int I420ToBGRA(const uint8* src_y, int src_stride_y,
541                const uint8* src_u, int src_stride_u,
542                const uint8* src_v, int src_stride_v,
543                uint8* dst_bgra, int dst_stride_bgra,
544                int width, int height) {
545   return I420ToRGBAMatrix(src_y, src_stride_y,
546                           src_v, src_stride_v,  // Swap U and V
547                           src_u, src_stride_u,
548                           dst_bgra, dst_stride_bgra,
549                           &kYvuI601Constants,  // Use Yvu matrix
550                           width, height);
551 }
552 
553 // Convert I420 to RGB24 with matrix
554 static int I420ToRGB24Matrix(const uint8* src_y, int src_stride_y,
555                              const uint8* src_u, int src_stride_u,
556                              const uint8* src_v, int src_stride_v,
557                              uint8* dst_rgb24, int dst_stride_rgb24,
558                              const struct YuvConstants* yuvconstants,
559                              int width, int height) {
560   int y;
561   void (*I422ToRGB24Row)(const uint8* y_buf,
562                          const uint8* u_buf,
563                          const uint8* v_buf,
564                          uint8* rgb_buf,
565                          const struct YuvConstants* yuvconstants,
566                          int width) = I422ToRGB24Row_C;
567   if (!src_y || !src_u || !src_v || !dst_rgb24 ||
568       width <= 0 || height == 0) {
569     return -1;
570   }
571   // Negative height means invert the image.
572   if (height < 0) {
573     height = -height;
574     dst_rgb24 = dst_rgb24 + (height - 1) * dst_stride_rgb24;
575     dst_stride_rgb24 = -dst_stride_rgb24;
576   }
577 #if defined(HAS_I422TORGB24ROW_SSSE3)
578   if (TestCpuFlag(kCpuHasSSSE3)) {
579     I422ToRGB24Row = I422ToRGB24Row_Any_SSSE3;
580     if (IS_ALIGNED(width, 8)) {
581       I422ToRGB24Row = I422ToRGB24Row_SSSE3;
582     }
583   }
584 #endif
585 #if defined(HAS_I422TORGB24ROW_AVX2)
586   if (TestCpuFlag(kCpuHasAVX2)) {
587     I422ToRGB24Row = I422ToRGB24Row_Any_AVX2;
588     if (IS_ALIGNED(width, 16)) {
589       I422ToRGB24Row = I422ToRGB24Row_AVX2;
590     }
591   }
592 #endif
593 #if defined(HAS_I422TORGB24ROW_NEON)
594   if (TestCpuFlag(kCpuHasNEON)) {
595     I422ToRGB24Row = I422ToRGB24Row_Any_NEON;
596     if (IS_ALIGNED(width, 8)) {
597       I422ToRGB24Row = I422ToRGB24Row_NEON;
598     }
599   }
600 #endif
601 
602   for (y = 0; y < height; ++y) {
603     I422ToRGB24Row(src_y, src_u, src_v, dst_rgb24, yuvconstants, width);
604     dst_rgb24 += dst_stride_rgb24;
605     src_y += src_stride_y;
606     if (y & 1) {
607       src_u += src_stride_u;
608       src_v += src_stride_v;
609     }
610   }
611   return 0;
612 }
613 
614 // Convert I420 to RGB24.
615 LIBYUV_API
616 int I420ToRGB24(const uint8* src_y, int src_stride_y,
617                 const uint8* src_u, int src_stride_u,
618                 const uint8* src_v, int src_stride_v,
619                 uint8* dst_rgb24, int dst_stride_rgb24,
620                 int width, int height) {
621   return I420ToRGB24Matrix(src_y, src_stride_y,
622                            src_u, src_stride_u,
623                            src_v, src_stride_v,
624                            dst_rgb24, dst_stride_rgb24,
625                            &kYuvI601Constants,
626                            width, height);
627 }
628 
629 // Convert I420 to RAW.
630 LIBYUV_API
631 int I420ToRAW(const uint8* src_y, int src_stride_y,
632               const uint8* src_u, int src_stride_u,
633               const uint8* src_v, int src_stride_v,
634               uint8* dst_raw, int dst_stride_raw,
635               int width, int height) {
636   return I420ToRGB24Matrix(src_y, src_stride_y,
637                            src_v, src_stride_v,  // Swap U and V
638                            src_u, src_stride_u,
639                            dst_raw, dst_stride_raw,
640                            &kYvuI601Constants,  // Use Yvu matrix
641                            width, height);
642 }
643 
644 // Convert I420 to ARGB1555.
645 LIBYUV_API
646 int I420ToARGB1555(const uint8* src_y, int src_stride_y,
647                    const uint8* src_u, int src_stride_u,
648                    const uint8* src_v, int src_stride_v,
649                    uint8* dst_argb1555, int dst_stride_argb1555,
650                    int width, int height) {
651   int y;
652   void (*I422ToARGB1555Row)(const uint8* y_buf,
653                             const uint8* u_buf,
654                             const uint8* v_buf,
655                             uint8* rgb_buf,
656                             const struct YuvConstants* yuvconstants,
657                             int width) = I422ToARGB1555Row_C;
658   if (!src_y || !src_u || !src_v || !dst_argb1555 ||
659       width <= 0 || height == 0) {
660     return -1;
661   }
662   // Negative height means invert the image.
663   if (height < 0) {
664     height = -height;
665     dst_argb1555 = dst_argb1555 + (height - 1) * dst_stride_argb1555;
666     dst_stride_argb1555 = -dst_stride_argb1555;
667   }
668 #if defined(HAS_I422TOARGB1555ROW_SSSE3)
669   if (TestCpuFlag(kCpuHasSSSE3)) {
670     I422ToARGB1555Row = I422ToARGB1555Row_Any_SSSE3;
671     if (IS_ALIGNED(width, 8)) {
672       I422ToARGB1555Row = I422ToARGB1555Row_SSSE3;
673     }
674   }
675 #endif
676 #if defined(HAS_I422TOARGB1555ROW_AVX2)
677   if (TestCpuFlag(kCpuHasAVX2)) {
678     I422ToARGB1555Row = I422ToARGB1555Row_Any_AVX2;
679     if (IS_ALIGNED(width, 16)) {
680       I422ToARGB1555Row = I422ToARGB1555Row_AVX2;
681     }
682   }
683 #endif
684 #if defined(HAS_I422TOARGB1555ROW_NEON)
685   if (TestCpuFlag(kCpuHasNEON)) {
686     I422ToARGB1555Row = I422ToARGB1555Row_Any_NEON;
687     if (IS_ALIGNED(width, 8)) {
688       I422ToARGB1555Row = I422ToARGB1555Row_NEON;
689     }
690   }
691 #endif
692 
693   for (y = 0; y < height; ++y) {
694     I422ToARGB1555Row(src_y, src_u, src_v, dst_argb1555, &kYuvI601Constants,
695                       width);
696     dst_argb1555 += dst_stride_argb1555;
697     src_y += src_stride_y;
698     if (y & 1) {
699       src_u += src_stride_u;
700       src_v += src_stride_v;
701     }
702   }
703   return 0;
704 }
705 
706 
707 // Convert I420 to ARGB4444.
708 LIBYUV_API
709 int I420ToARGB4444(const uint8* src_y, int src_stride_y,
710                    const uint8* src_u, int src_stride_u,
711                    const uint8* src_v, int src_stride_v,
712                    uint8* dst_argb4444, int dst_stride_argb4444,
713                    int width, int height) {
714   int y;
715   void (*I422ToARGB4444Row)(const uint8* y_buf,
716                             const uint8* u_buf,
717                             const uint8* v_buf,
718                             uint8* rgb_buf,
719                             const struct YuvConstants* yuvconstants,
720                             int width) = I422ToARGB4444Row_C;
721   if (!src_y || !src_u || !src_v || !dst_argb4444 ||
722       width <= 0 || height == 0) {
723     return -1;
724   }
725   // Negative height means invert the image.
726   if (height < 0) {
727     height = -height;
728     dst_argb4444 = dst_argb4444 + (height - 1) * dst_stride_argb4444;
729     dst_stride_argb4444 = -dst_stride_argb4444;
730   }
731 #if defined(HAS_I422TOARGB4444ROW_SSSE3)
732   if (TestCpuFlag(kCpuHasSSSE3)) {
733     I422ToARGB4444Row = I422ToARGB4444Row_Any_SSSE3;
734     if (IS_ALIGNED(width, 8)) {
735       I422ToARGB4444Row = I422ToARGB4444Row_SSSE3;
736     }
737   }
738 #endif
739 #if defined(HAS_I422TOARGB4444ROW_AVX2)
740   if (TestCpuFlag(kCpuHasAVX2)) {
741     I422ToARGB4444Row = I422ToARGB4444Row_Any_AVX2;
742     if (IS_ALIGNED(width, 16)) {
743       I422ToARGB4444Row = I422ToARGB4444Row_AVX2;
744     }
745   }
746 #endif
747 #if defined(HAS_I422TOARGB4444ROW_NEON)
748   if (TestCpuFlag(kCpuHasNEON)) {
749     I422ToARGB4444Row = I422ToARGB4444Row_Any_NEON;
750     if (IS_ALIGNED(width, 8)) {
751       I422ToARGB4444Row = I422ToARGB4444Row_NEON;
752     }
753   }
754 #endif
755 
756   for (y = 0; y < height; ++y) {
757     I422ToARGB4444Row(src_y, src_u, src_v, dst_argb4444, &kYuvI601Constants,
758                       width);
759     dst_argb4444 += dst_stride_argb4444;
760     src_y += src_stride_y;
761     if (y & 1) {
762       src_u += src_stride_u;
763       src_v += src_stride_v;
764     }
765   }
766   return 0;
767 }
768 
769 // Convert I420 to RGB565.
770 LIBYUV_API
771 int I420ToRGB565(const uint8* src_y, int src_stride_y,
772                  const uint8* src_u, int src_stride_u,
773                  const uint8* src_v, int src_stride_v,
774                  uint8* dst_rgb565, int dst_stride_rgb565,
775                  int width, int height) {
776   int y;
777   void (*I422ToRGB565Row)(const uint8* y_buf,
778                           const uint8* u_buf,
779                           const uint8* v_buf,
780                           uint8* rgb_buf,
781                           const struct YuvConstants* yuvconstants,
782                           int width) = I422ToRGB565Row_C;
783   if (!src_y || !src_u || !src_v || !dst_rgb565 ||
784       width <= 0 || height == 0) {
785     return -1;
786   }
787   // Negative height means invert the image.
788   if (height < 0) {
789     height = -height;
790     dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
791     dst_stride_rgb565 = -dst_stride_rgb565;
792   }
793 #if defined(HAS_I422TORGB565ROW_SSSE3)
794   if (TestCpuFlag(kCpuHasSSSE3)) {
795     I422ToRGB565Row = I422ToRGB565Row_Any_SSSE3;
796     if (IS_ALIGNED(width, 8)) {
797       I422ToRGB565Row = I422ToRGB565Row_SSSE3;
798     }
799   }
800 #endif
801 #if defined(HAS_I422TORGB565ROW_AVX2)
802   if (TestCpuFlag(kCpuHasAVX2)) {
803     I422ToRGB565Row = I422ToRGB565Row_Any_AVX2;
804     if (IS_ALIGNED(width, 16)) {
805       I422ToRGB565Row = I422ToRGB565Row_AVX2;
806     }
807   }
808 #endif
809 #if defined(HAS_I422TORGB565ROW_NEON)
810   if (TestCpuFlag(kCpuHasNEON)) {
811     I422ToRGB565Row = I422ToRGB565Row_Any_NEON;
812     if (IS_ALIGNED(width, 8)) {
813       I422ToRGB565Row = I422ToRGB565Row_NEON;
814     }
815   }
816 #endif
817 
818   for (y = 0; y < height; ++y) {
819     I422ToRGB565Row(src_y, src_u, src_v, dst_rgb565, &kYuvI601Constants, width);
820     dst_rgb565 += dst_stride_rgb565;
821     src_y += src_stride_y;
822     if (y & 1) {
823       src_u += src_stride_u;
824       src_v += src_stride_v;
825     }
826   }
827   return 0;
828 }
829 
830 // Ordered 8x8 dither for 888 to 565.  Values from 0 to 7.
831 static const uint8 kDither565_4x4[16] = {
832   0, 4, 1, 5,
833   6, 2, 7, 3,
834   1, 5, 0, 4,
835   7, 3, 6, 2,
836 };
837 
838 // Convert I420 to RGB565 with dithering.
839 LIBYUV_API
840 int I420ToRGB565Dither(const uint8* src_y, int src_stride_y,
841                        const uint8* src_u, int src_stride_u,
842                        const uint8* src_v, int src_stride_v,
843                        uint8* dst_rgb565, int dst_stride_rgb565,
844                        const uint8* dither4x4, int width, int height) {
845   int y;
846   void (*I422ToARGBRow)(const uint8* y_buf,
847                         const uint8* u_buf,
848                         const uint8* v_buf,
849                         uint8* rgb_buf,
850                         const struct YuvConstants* yuvconstants,
851                         int width) = I422ToARGBRow_C;
852   void (*ARGBToRGB565DitherRow)(const uint8* src_argb, uint8* dst_rgb,
853       const uint32 dither4, int width) = ARGBToRGB565DitherRow_C;
854   if (!src_y || !src_u || !src_v || !dst_rgb565 ||
855       width <= 0 || height == 0) {
856     return -1;
857   }
858   // Negative height means invert the image.
859   if (height < 0) {
860     height = -height;
861     dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
862     dst_stride_rgb565 = -dst_stride_rgb565;
863   }
864   if (!dither4x4) {
865     dither4x4 = kDither565_4x4;
866   }
867 #if defined(HAS_I422TOARGBROW_SSSE3)
868   if (TestCpuFlag(kCpuHasSSSE3)) {
869     I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
870     if (IS_ALIGNED(width, 8)) {
871       I422ToARGBRow = I422ToARGBRow_SSSE3;
872     }
873   }
874 #endif
875 #if defined(HAS_I422TOARGBROW_AVX2)
876   if (TestCpuFlag(kCpuHasAVX2)) {
877     I422ToARGBRow = I422ToARGBRow_Any_AVX2;
878     if (IS_ALIGNED(width, 16)) {
879       I422ToARGBRow = I422ToARGBRow_AVX2;
880     }
881   }
882 #endif
883 #if defined(HAS_I422TOARGBROW_NEON)
884   if (TestCpuFlag(kCpuHasNEON)) {
885     I422ToARGBRow = I422ToARGBRow_Any_NEON;
886     if (IS_ALIGNED(width, 8)) {
887       I422ToARGBRow = I422ToARGBRow_NEON;
888     }
889   }
890 #endif
891 #if defined(HAS_I422TOARGBROW_DSPR2)
892   if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(width, 4) &&
893       IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) &&
894       IS_ALIGNED(src_u, 2) && IS_ALIGNED(src_stride_u, 2) &&
895       IS_ALIGNED(src_v, 2) && IS_ALIGNED(src_stride_v, 2)) {
896     I422ToARGBRow = I422ToARGBRow_DSPR2;
897   }
898 #endif
899 #if defined(HAS_ARGBTORGB565DITHERROW_SSE2)
900   if (TestCpuFlag(kCpuHasSSE2)) {
901     ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_SSE2;
902     if (IS_ALIGNED(width, 4)) {
903       ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_SSE2;
904     }
905   }
906 #endif
907 #if defined(HAS_ARGBTORGB565DITHERROW_AVX2)
908   if (TestCpuFlag(kCpuHasAVX2)) {
909     ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_AVX2;
910     if (IS_ALIGNED(width, 8)) {
911       ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_AVX2;
912     }
913   }
914 #endif
915 #if defined(HAS_ARGBTORGB565DITHERROW_NEON)
916   if (TestCpuFlag(kCpuHasNEON)) {
917     ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_Any_NEON;
918     if (IS_ALIGNED(width, 8)) {
919       ARGBToRGB565DitherRow = ARGBToRGB565DitherRow_NEON;
920     }
921   }
922 #endif
923   {
924     // Allocate a row of argb.
925     align_buffer_64(row_argb, width * 4);
926     for (y = 0; y < height; ++y) {
927       I422ToARGBRow(src_y, src_u, src_v, row_argb, &kYuvI601Constants, width);
928       ARGBToRGB565DitherRow(row_argb, dst_rgb565,
929                             *(uint32*)(dither4x4 + ((y & 3) << 2)), width);
930       dst_rgb565 += dst_stride_rgb565;
931       src_y += src_stride_y;
932       if (y & 1) {
933         src_u += src_stride_u;
934         src_v += src_stride_v;
935       }
936     }
937     free_aligned_buffer_64(row_argb);
938   }
939   return 0;
940 }
941 
942 // Convert I420 to specified format
943 LIBYUV_API
944 int ConvertFromI420(const uint8* y, int y_stride,
945                     const uint8* u, int u_stride,
946                     const uint8* v, int v_stride,
947                     uint8* dst_sample, int dst_sample_stride,
948                     int width, int height,
949                     uint32 fourcc) {
950   uint32 format = CanonicalFourCC(fourcc);
951   int r = 0;
952   if (!y || !u|| !v || !dst_sample ||
953       width <= 0 || height == 0) {
954     return -1;
955   }
956   switch (format) {
957     // Single plane formats
958     case FOURCC_YUY2:
959       r = I420ToYUY2(y, y_stride,
960                      u, u_stride,
961                      v, v_stride,
962                      dst_sample,
963                      dst_sample_stride ? dst_sample_stride : width * 2,
964                      width, height);
965       break;
966     case FOURCC_UYVY:
967       r = I420ToUYVY(y, y_stride,
968                      u, u_stride,
969                      v, v_stride,
970                      dst_sample,
971                      dst_sample_stride ? dst_sample_stride : width * 2,
972                      width, height);
973       break;
974     case FOURCC_RGBP:
975       r = I420ToRGB565(y, y_stride,
976                        u, u_stride,
977                        v, v_stride,
978                        dst_sample,
979                        dst_sample_stride ? dst_sample_stride : width * 2,
980                        width, height);
981       break;
982     case FOURCC_RGBO:
983       r = I420ToARGB1555(y, y_stride,
984                          u, u_stride,
985                          v, v_stride,
986                          dst_sample,
987                          dst_sample_stride ? dst_sample_stride : width * 2,
988                          width, height);
989       break;
990     case FOURCC_R444:
991       r = I420ToARGB4444(y, y_stride,
992                          u, u_stride,
993                          v, v_stride,
994                          dst_sample,
995                          dst_sample_stride ? dst_sample_stride : width * 2,
996                          width, height);
997       break;
998     case FOURCC_24BG:
999       r = I420ToRGB24(y, y_stride,
1000                       u, u_stride,
1001                       v, v_stride,
1002                       dst_sample,
1003                       dst_sample_stride ? dst_sample_stride : width * 3,
1004                       width, height);
1005       break;
1006     case FOURCC_RAW:
1007       r = I420ToRAW(y, y_stride,
1008                     u, u_stride,
1009                     v, v_stride,
1010                     dst_sample,
1011                     dst_sample_stride ? dst_sample_stride : width * 3,
1012                     width, height);
1013       break;
1014     case FOURCC_ARGB:
1015       r = I420ToARGB(y, y_stride,
1016                      u, u_stride,
1017                      v, v_stride,
1018                      dst_sample,
1019                      dst_sample_stride ? dst_sample_stride : width * 4,
1020                      width, height);
1021       break;
1022     case FOURCC_BGRA:
1023       r = I420ToBGRA(y, y_stride,
1024                      u, u_stride,
1025                      v, v_stride,
1026                      dst_sample,
1027                      dst_sample_stride ? dst_sample_stride : width * 4,
1028                      width, height);
1029       break;
1030     case FOURCC_ABGR:
1031       r = I420ToABGR(y, y_stride,
1032                      u, u_stride,
1033                      v, v_stride,
1034                      dst_sample,
1035                      dst_sample_stride ? dst_sample_stride : width * 4,
1036                      width, height);
1037       break;
1038     case FOURCC_RGBA:
1039       r = I420ToRGBA(y, y_stride,
1040                      u, u_stride,
1041                      v, v_stride,
1042                      dst_sample,
1043                      dst_sample_stride ? dst_sample_stride : width * 4,
1044                      width, height);
1045       break;
1046     case FOURCC_I400:
1047       r = I400Copy(y, y_stride,
1048                    dst_sample,
1049                    dst_sample_stride ? dst_sample_stride : width,
1050                    width, height);
1051       break;
1052     case FOURCC_NV12: {
1053       uint8* dst_uv = dst_sample + width * height;
1054       r = I420ToNV12(y, y_stride,
1055                      u, u_stride,
1056                      v, v_stride,
1057                      dst_sample,
1058                      dst_sample_stride ? dst_sample_stride : width,
1059                      dst_uv,
1060                      dst_sample_stride ? dst_sample_stride : width,
1061                      width, height);
1062       break;
1063     }
1064     case FOURCC_NV21: {
1065       uint8* dst_vu = dst_sample + width * height;
1066       r = I420ToNV21(y, y_stride,
1067                      u, u_stride,
1068                      v, v_stride,
1069                      dst_sample,
1070                      dst_sample_stride ? dst_sample_stride : width,
1071                      dst_vu,
1072                      dst_sample_stride ? dst_sample_stride : width,
1073                      width, height);
1074       break;
1075     }
1076     // TODO(fbarchard): Add M420.
1077     // Triplanar formats
1078     // TODO(fbarchard): halfstride instead of halfwidth
1079     case FOURCC_I420:
1080     case FOURCC_YV12: {
1081       int halfwidth = (width + 1) / 2;
1082       int halfheight = (height + 1) / 2;
1083       uint8* dst_u;
1084       uint8* dst_v;
1085       if (format == FOURCC_YV12) {
1086         dst_v = dst_sample + width * height;
1087         dst_u = dst_v + halfwidth * halfheight;
1088       } else {
1089         dst_u = dst_sample + width * height;
1090         dst_v = dst_u + halfwidth * halfheight;
1091       }
1092       r = I420Copy(y, y_stride,
1093                    u, u_stride,
1094                    v, v_stride,
1095                    dst_sample, width,
1096                    dst_u, halfwidth,
1097                    dst_v, halfwidth,
1098                    width, height);
1099       break;
1100     }
1101     case FOURCC_I422:
1102     case FOURCC_YV16: {
1103       int halfwidth = (width + 1) / 2;
1104       uint8* dst_u;
1105       uint8* dst_v;
1106       if (format == FOURCC_YV16) {
1107         dst_v = dst_sample + width * height;
1108         dst_u = dst_v + halfwidth * height;
1109       } else {
1110         dst_u = dst_sample + width * height;
1111         dst_v = dst_u + halfwidth * height;
1112       }
1113       r = I420ToI422(y, y_stride,
1114                      u, u_stride,
1115                      v, v_stride,
1116                      dst_sample, width,
1117                      dst_u, halfwidth,
1118                      dst_v, halfwidth,
1119                      width, height);
1120       break;
1121     }
1122     case FOURCC_I444:
1123     case FOURCC_YV24: {
1124       uint8* dst_u;
1125       uint8* dst_v;
1126       if (format == FOURCC_YV24) {
1127         dst_v = dst_sample + width * height;
1128         dst_u = dst_v + width * height;
1129       } else {
1130         dst_u = dst_sample + width * height;
1131         dst_v = dst_u + width * height;
1132       }
1133       r = I420ToI444(y, y_stride,
1134                      u, u_stride,
1135                      v, v_stride,
1136                      dst_sample, width,
1137                      dst_u, width,
1138                      dst_v, width,
1139                      width, height);
1140       break;
1141     }
1142     case FOURCC_I411: {
1143       int quarterwidth = (width + 3) / 4;
1144       uint8* dst_u = dst_sample + width * height;
1145       uint8* dst_v = dst_u + quarterwidth * height;
1146       r = I420ToI411(y, y_stride,
1147                      u, u_stride,
1148                      v, v_stride,
1149                      dst_sample, width,
1150                      dst_u, quarterwidth,
1151                      dst_v, quarterwidth,
1152                      width, height);
1153       break;
1154     }
1155 
1156     // Formats not supported - MJPG, biplanar, some rgb formats.
1157     default:
1158       return -1;  // unknown fourcc - return failure code.
1159   }
1160   return r;
1161 }
1162 
1163 #ifdef __cplusplus
1164 }  // extern "C"
1165 }  // namespace libyuv
1166 #endif
1167