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
23namespace libyuv {
24extern "C" {
25#endif
26
27#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
28static __inline int Abs(int v) {
29  return v >= 0 ? v : -v;
30}
31
32// I420 To any I4xx YUV format with mirroring.
33static 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
63LIBYUV_API
64int 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
85LIBYUV_API
86int 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
107LIBYUV_API
108int 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
128LIBYUV_API
129int 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
146LIBYUV_API
147int 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
202LIBYUV_API
203int 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
254LIBYUV_API
255int 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
310LIBYUV_API
311int 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
362LIBYUV_API
363int 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
438LIBYUV_API
439int 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
454static 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.
524LIBYUV_API
525int 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.
539LIBYUV_API
540int 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
554static 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.
615LIBYUV_API
616int 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.
630LIBYUV_API
631int 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.
645LIBYUV_API
646int 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.
708LIBYUV_API
709int 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.
770LIBYUV_API
771int 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.
831static 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.
839LIBYUV_API
840int 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
943LIBYUV_API
944int 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