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_argb.h"
12
13#include <string.h>  // for memset()
14
15#include "libyuv/cpu_id.h"
16#include "libyuv/format_conversion.h"
17#ifdef HAVE_JPEG
18#include "libyuv/mjpeg_decoder.h"
19#endif
20#include "libyuv/rotate_argb.h"
21#include "libyuv/video_common.h"
22#include "libyuv/row.h"
23
24#ifdef __cplusplus
25namespace libyuv {
26extern "C" {
27#endif
28
29// Copy ARGB with optional flipping
30LIBYUV_API
31int ARGBCopy(const uint8* src_argb, int src_stride_argb,
32             uint8* dst_argb, int dst_stride_argb,
33             int width, int height) {
34  if (!src_argb || !dst_argb ||
35      width <= 0 || height == 0) {
36    return -1;
37  }
38  // Negative height means invert the image.
39  if (height < 0) {
40    height = -height;
41    src_argb = src_argb + (height - 1) * src_stride_argb;
42    src_stride_argb = -src_stride_argb;
43  }
44
45  CopyPlane(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
46            width * 4, height);
47  return 0;
48}
49
50// Convert I444 to ARGB.
51LIBYUV_API
52int I444ToARGB(const uint8* src_y, int src_stride_y,
53               const uint8* src_u, int src_stride_u,
54               const uint8* src_v, int src_stride_v,
55               uint8* dst_argb, int dst_stride_argb,
56               int width, int height) {
57  if (!src_y || !src_u || !src_v ||
58      !dst_argb ||
59      width <= 0 || height == 0) {
60    return -1;
61  }
62  // Negative height means invert the image.
63  if (height < 0) {
64    height = -height;
65    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
66    dst_stride_argb = -dst_stride_argb;
67  }
68  void (*I444ToARGBRow)(const uint8* y_buf,
69                        const uint8* u_buf,
70                        const uint8* v_buf,
71                        uint8* rgb_buf,
72                        int width) = I444ToARGBRow_C;
73#if defined(HAS_I444TOARGBROW_SSSE3)
74  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
75    I444ToARGBRow = I444ToARGBRow_Any_SSSE3;
76    if (IS_ALIGNED(width, 8)) {
77      I444ToARGBRow = I444ToARGBRow_Unaligned_SSSE3;
78      if (IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
79        I444ToARGBRow = I444ToARGBRow_SSSE3;
80      }
81    }
82  }
83#endif
84
85  for (int y = 0; y < height; ++y) {
86    I444ToARGBRow(src_y, src_u, src_v, dst_argb, width);
87    dst_argb += dst_stride_argb;
88    src_y += src_stride_y;
89    src_u += src_stride_u;
90    src_v += src_stride_v;
91  }
92  return 0;
93}
94
95// Convert I422 to ARGB.
96LIBYUV_API
97int I422ToARGB(const uint8* src_y, int src_stride_y,
98               const uint8* src_u, int src_stride_u,
99               const uint8* src_v, int src_stride_v,
100               uint8* dst_argb, int dst_stride_argb,
101               int width, int height) {
102  if (!src_y || !src_u || !src_v ||
103      !dst_argb ||
104      width <= 0 || height == 0) {
105    return -1;
106  }
107  // Negative height means invert the image.
108  if (height < 0) {
109    height = -height;
110    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
111    dst_stride_argb = -dst_stride_argb;
112  }
113  void (*I422ToARGBRow)(const uint8* y_buf,
114                        const uint8* u_buf,
115                        const uint8* v_buf,
116                        uint8* rgb_buf,
117                        int width) = I422ToARGBRow_C;
118#if defined(HAS_I422TOARGBROW_NEON)
119  if (TestCpuFlag(kCpuHasNEON)) {
120    I422ToARGBRow = I422ToARGBRow_Any_NEON;
121    if (IS_ALIGNED(width, 16)) {
122      I422ToARGBRow = I422ToARGBRow_NEON;
123    }
124  }
125#elif defined(HAS_I422TOARGBROW_SSSE3)
126  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
127    I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
128    if (IS_ALIGNED(width, 8)) {
129      I422ToARGBRow = I422ToARGBRow_Unaligned_SSSE3;
130      if (IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
131        I422ToARGBRow = I422ToARGBRow_SSSE3;
132      }
133    }
134  }
135#endif
136
137  for (int y = 0; y < height; ++y) {
138    I422ToARGBRow(src_y, src_u, src_v, dst_argb, width);
139    dst_argb += dst_stride_argb;
140    src_y += src_stride_y;
141    src_u += src_stride_u;
142    src_v += src_stride_v;
143  }
144  return 0;
145}
146
147// Convert I411 to ARGB.
148LIBYUV_API
149int I411ToARGB(const uint8* src_y, int src_stride_y,
150               const uint8* src_u, int src_stride_u,
151               const uint8* src_v, int src_stride_v,
152               uint8* dst_argb, int dst_stride_argb,
153               int width, int height) {
154  if (!src_y || !src_u || !src_v ||
155      !dst_argb ||
156      width <= 0 || height == 0) {
157    return -1;
158  }
159  // Negative height means invert the image.
160  if (height < 0) {
161    height = -height;
162    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
163    dst_stride_argb = -dst_stride_argb;
164  }
165  void (*I411ToARGBRow)(const uint8* y_buf,
166                        const uint8* u_buf,
167                        const uint8* v_buf,
168                        uint8* rgb_buf,
169                        int width) = I411ToARGBRow_C;
170#if defined(HAS_I411TOARGBROW_SSSE3)
171  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
172    I411ToARGBRow = I411ToARGBRow_Any_SSSE3;
173    if (IS_ALIGNED(width, 8)) {
174      I411ToARGBRow = I411ToARGBRow_Unaligned_SSSE3;
175      if (IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
176        I411ToARGBRow = I411ToARGBRow_SSSE3;
177      }
178    }
179  }
180#endif
181
182  for (int y = 0; y < height; ++y) {
183    I411ToARGBRow(src_y, src_u, src_v, dst_argb, width);
184    dst_argb += dst_stride_argb;
185    src_y += src_stride_y;
186    src_u += src_stride_u;
187    src_v += src_stride_v;
188  }
189  return 0;
190}
191
192
193// Convert I400 to ARGB.
194LIBYUV_API
195int I400ToARGB_Reference(const uint8* src_y, int src_stride_y,
196                         uint8* dst_argb, int dst_stride_argb,
197                         int width, int height) {
198  if (!src_y || !dst_argb ||
199      width <= 0 || height == 0) {
200    return -1;
201  }
202  // Negative height means invert the image.
203  if (height < 0) {
204    height = -height;
205    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
206    dst_stride_argb = -dst_stride_argb;
207  }
208  void (*YToARGBRow)(const uint8* y_buf,
209                     uint8* rgb_buf,
210                     int width) = YToARGBRow_C;
211#if defined(HAS_YTOARGBROW_SSE2)
212  if (TestCpuFlag(kCpuHasSSE2) &&
213      IS_ALIGNED(width, 8) &&
214      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
215    YToARGBRow = YToARGBRow_SSE2;
216  }
217#endif
218
219  for (int y = 0; y < height; ++y) {
220    YToARGBRow(src_y, dst_argb, width);
221    dst_argb += dst_stride_argb;
222    src_y += src_stride_y;
223  }
224  return 0;
225}
226
227// Convert I400 to ARGB.
228LIBYUV_API
229int I400ToARGB(const uint8* src_y, int src_stride_y,
230               uint8* dst_argb, int dst_stride_argb,
231               int width, int height) {
232  if (!src_y || !dst_argb ||
233      width <= 0 || height == 0) {
234    return -1;
235  }
236  // Negative height means invert the image.
237  if (height < 0) {
238    height = -height;
239    src_y = src_y + (height - 1) * src_stride_y;
240    src_stride_y = -src_stride_y;
241  }
242  void (*I400ToARGBRow)(const uint8* src_y, uint8* dst_argb, int pix) =
243      I400ToARGBRow_C;
244#if defined(HAS_I400TOARGBROW_SSE2)
245  if (TestCpuFlag(kCpuHasSSE2) &&
246      IS_ALIGNED(width, 8) &&
247      IS_ALIGNED(src_y, 8) && IS_ALIGNED(src_stride_y, 8) &&
248      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
249    I400ToARGBRow = I400ToARGBRow_SSE2;
250  }
251#endif
252
253  for (int y = 0; y < height; ++y) {
254    I400ToARGBRow(src_y, dst_argb, width);
255    src_y += src_stride_y;
256    dst_argb += dst_stride_argb;
257  }
258  return 0;
259}
260
261// Convert BGRA to ARGB.
262LIBYUV_API
263int BGRAToARGB(const uint8* src_bgra, int src_stride_bgra,
264               uint8* dst_argb, int dst_stride_argb,
265               int width, int height) {
266  if (!src_bgra || !dst_argb ||
267      width <= 0 || height == 0) {
268    return -1;
269  }
270  // Negative height means invert the image.
271  if (height < 0) {
272    height = -height;
273    src_bgra = src_bgra + (height - 1) * src_stride_bgra;
274    src_stride_bgra = -src_stride_bgra;
275  }
276  void (*BGRAToARGBRow)(const uint8* src_bgra, uint8* dst_argb, int pix) =
277      BGRAToARGBRow_C;
278#if defined(HAS_BGRATOARGBROW_SSSE3)
279  if (TestCpuFlag(kCpuHasSSSE3) &&
280      IS_ALIGNED(width, 4) &&
281      IS_ALIGNED(src_bgra, 16) && IS_ALIGNED(src_stride_bgra, 16) &&
282      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
283    BGRAToARGBRow = BGRAToARGBRow_SSSE3;
284  }
285#endif
286
287  for (int y = 0; y < height; ++y) {
288    BGRAToARGBRow(src_bgra, dst_argb, width);
289    src_bgra += src_stride_bgra;
290    dst_argb += dst_stride_argb;
291  }
292  return 0;
293}
294
295// Convert ABGR to ARGB.
296LIBYUV_API
297int ABGRToARGB(const uint8* src_abgr, int src_stride_abgr,
298               uint8* dst_argb, int dst_stride_argb,
299               int width, int height) {
300  if (!src_abgr || !dst_argb ||
301      width <= 0 || height == 0) {
302    return -1;
303  }
304  // Negative height means invert the image.
305  if (height < 0) {
306    height = -height;
307    src_abgr = src_abgr + (height - 1) * src_stride_abgr;
308    src_stride_abgr = -src_stride_abgr;
309  }
310  void (*ABGRToARGBRow)(const uint8* src_abgr, uint8* dst_argb, int pix) =
311      ABGRToARGBRow_C;
312#if defined(HAS_ABGRTOARGBROW_SSSE3)
313  if (TestCpuFlag(kCpuHasSSSE3) &&
314      IS_ALIGNED(width, 4) &&
315      IS_ALIGNED(src_abgr, 16) && IS_ALIGNED(src_stride_abgr, 16) &&
316      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
317    ABGRToARGBRow = ABGRToARGBRow_SSSE3;
318  }
319#endif
320
321  for (int y = 0; y < height; ++y) {
322    ABGRToARGBRow(src_abgr, dst_argb, width);
323    src_abgr += src_stride_abgr;
324    dst_argb += dst_stride_argb;
325  }
326  return 0;
327}
328
329// Convert RGBA to ARGB.
330LIBYUV_API
331int RGBAToARGB(const uint8* src_rgba, int src_stride_rgba,
332               uint8* dst_argb, int dst_stride_argb,
333               int width, int height) {
334  if (!src_rgba || !dst_argb ||
335      width <= 0 || height == 0) {
336    return -1;
337  }
338  // Negative height means invert the image.
339  if (height < 0) {
340    height = -height;
341    src_rgba = src_rgba + (height - 1) * src_stride_rgba;
342    src_stride_rgba = -src_stride_rgba;
343  }
344  void (*RGBAToARGBRow)(const uint8* src_rgba, uint8* dst_argb, int pix) =
345      RGBAToARGBRow_C;
346#if defined(HAS_RGBATOARGBROW_SSSE3)
347  if (TestCpuFlag(kCpuHasSSSE3) &&
348      IS_ALIGNED(width, 4) &&
349      IS_ALIGNED(src_rgba, 16) && IS_ALIGNED(src_stride_rgba, 16) &&
350      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
351    RGBAToARGBRow = RGBAToARGBRow_SSSE3;
352  }
353#endif
354
355  for (int y = 0; y < height; ++y) {
356    RGBAToARGBRow(src_rgba, dst_argb, width);
357    src_rgba += src_stride_rgba;
358    dst_argb += dst_stride_argb;
359  }
360  return 0;
361}
362
363// Convert RAW to ARGB.
364LIBYUV_API
365int RAWToARGB(const uint8* src_raw, int src_stride_raw,
366              uint8* dst_argb, int dst_stride_argb,
367              int width, int height) {
368  if (!src_raw || !dst_argb ||
369      width <= 0 || height == 0) {
370    return -1;
371  }
372  // Negative height means invert the image.
373  if (height < 0) {
374    height = -height;
375    src_raw = src_raw + (height - 1) * src_stride_raw;
376    src_stride_raw = -src_stride_raw;
377  }
378  void (*RAWToARGBRow)(const uint8* src_raw, uint8* dst_argb, int pix) =
379      RAWToARGBRow_C;
380#if defined(HAS_RAWTOARGBROW_SSSE3)
381  if (TestCpuFlag(kCpuHasSSSE3) &&
382      IS_ALIGNED(width, 16) &&
383      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
384    RAWToARGBRow = RAWToARGBRow_SSSE3;
385  }
386#endif
387
388  for (int y = 0; y < height; ++y) {
389    RAWToARGBRow(src_raw, dst_argb, width);
390    src_raw += src_stride_raw;
391    dst_argb += dst_stride_argb;
392  }
393  return 0;
394}
395
396// Convert RGB24 to ARGB.
397LIBYUV_API
398int RGB24ToARGB(const uint8* src_rgb24, int src_stride_rgb24,
399                uint8* dst_argb, int dst_stride_argb,
400                int width, int height) {
401  if (!src_rgb24 || !dst_argb ||
402      width <= 0 || height == 0) {
403    return -1;
404  }
405  // Negative height means invert the image.
406  if (height < 0) {
407    height = -height;
408    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
409    src_stride_rgb24 = -src_stride_rgb24;
410  }
411  void (*RGB24ToARGBRow)(const uint8* src_rgb24, uint8* dst_argb, int pix) =
412      RGB24ToARGBRow_C;
413#if defined(HAS_RGB24TOARGBROW_SSSE3)
414  if (TestCpuFlag(kCpuHasSSSE3) &&
415      IS_ALIGNED(width, 16) &&
416      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
417    RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
418  }
419#endif
420
421  for (int y = 0; y < height; ++y) {
422    RGB24ToARGBRow(src_rgb24, dst_argb, width);
423    src_rgb24 += src_stride_rgb24;
424    dst_argb += dst_stride_argb;
425  }
426  return 0;
427}
428
429// Convert RGB565 to ARGB.
430LIBYUV_API
431int RGB565ToARGB(const uint8* src_rgb565, int src_stride_rgb565,
432                 uint8* dst_argb, int dst_stride_argb,
433                 int width, int height) {
434  if (!src_rgb565 || !dst_argb ||
435      width <= 0 || height == 0) {
436    return -1;
437  }
438  // Negative height means invert the image.
439  if (height < 0) {
440    height = -height;
441    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
442    src_stride_rgb565 = -src_stride_rgb565;
443  }
444  void (*RGB565ToARGBRow)(const uint8* src_rgb565, uint8* dst_argb, int pix) =
445      RGB565ToARGBRow_C;
446#if defined(HAS_RGB565TOARGBROW_SSE2)
447  if (TestCpuFlag(kCpuHasSSE2) &&
448      IS_ALIGNED(width, 8) &&
449      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
450    RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
451  }
452#endif
453
454  for (int y = 0; y < height; ++y) {
455    RGB565ToARGBRow(src_rgb565, dst_argb, width);
456    src_rgb565 += src_stride_rgb565;
457    dst_argb += dst_stride_argb;
458  }
459  return 0;
460}
461
462// Convert ARGB1555 to ARGB.
463LIBYUV_API
464int ARGB1555ToARGB(const uint8* src_argb1555, int src_stride_argb1555,
465                   uint8* dst_argb, int dst_stride_argb,
466                   int width, int height) {
467  if (!src_argb1555 || !dst_argb ||
468       width <= 0 || height == 0) {
469    return -1;
470  }
471  // Negative height means invert the image.
472  if (height < 0) {
473    height = -height;
474    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
475    src_stride_argb1555 = -src_stride_argb1555;
476  }
477  void (*ARGB1555ToARGBRow)(const uint8* src_argb1555, uint8* dst_argb,
478                            int pix) = ARGB1555ToARGBRow_C;
479#if defined(HAS_ARGB1555TOARGBROW_SSE2)
480  if (TestCpuFlag(kCpuHasSSE2) &&
481      IS_ALIGNED(width, 8) &&
482      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
483    ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
484  }
485#endif
486
487  for (int y = 0; y < height; ++y) {
488    ARGB1555ToARGBRow(src_argb1555, dst_argb, width);
489    src_argb1555 += src_stride_argb1555;
490    dst_argb += dst_stride_argb;
491  }
492  return 0;
493}
494
495// Convert ARGB4444 to ARGB.
496LIBYUV_API
497int ARGB4444ToARGB(const uint8* src_argb4444, int src_stride_argb4444,
498                   uint8* dst_argb, int dst_stride_argb,
499                   int width, int height) {
500  if (!src_argb4444 || !dst_argb ||
501      width <= 0 || height == 0) {
502    return -1;
503  }
504  // Negative height means invert the image.
505  if (height < 0) {
506    height = -height;
507    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
508    src_stride_argb4444 = -src_stride_argb4444;
509  }
510  void (*ARGB4444ToARGBRow)(const uint8* src_argb4444, uint8* dst_argb,
511                            int pix) = ARGB4444ToARGBRow_C;
512#if defined(HAS_ARGB4444TOARGBROW_SSE2)
513  if (TestCpuFlag(kCpuHasSSE2) &&
514      IS_ALIGNED(width, 8) &&
515      IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
516    ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
517  }
518#endif
519
520  for (int y = 0; y < height; ++y) {
521    ARGB4444ToARGBRow(src_argb4444, dst_argb, width);
522    src_argb4444 += src_stride_argb4444;
523    dst_argb += dst_stride_argb;
524  }
525  return 0;
526}
527
528// Convert NV12 to ARGB.
529LIBYUV_API
530int NV12ToARGB(const uint8* src_y, int src_stride_y,
531               const uint8* src_uv, int src_stride_uv,
532               uint8* dst_argb, int dst_stride_argb,
533               int width, int height) {
534  if (!src_y || !src_uv || !dst_argb ||
535      width <= 0 || height == 0) {
536    return -1;
537  }
538  // Negative height means invert the image.
539  if (height < 0) {
540    height = -height;
541    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
542    dst_stride_argb = -dst_stride_argb;
543  }
544  void (*NV12ToARGBRow)(const uint8* y_buf,
545                        const uint8* uv_buf,
546                        uint8* rgb_buf,
547                        int width) = NV12ToARGBRow_C;
548#if defined(HAS_NV12TOARGBROW_SSSE3)
549  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
550    NV12ToARGBRow = NV12ToARGBRow_Any_SSSE3;
551    if (IS_ALIGNED(width, 8)) {
552      NV12ToARGBRow = NV12ToARGBRow_Unaligned_SSSE3;
553      if (IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
554        NV12ToARGBRow = NV12ToARGBRow_SSSE3;
555      }
556    }
557  }
558#endif
559#if defined(HAS_NV12TOARGBROW_NEON)
560  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
561    NV12ToARGBRow = NV12ToARGBRow_Any_NEON;
562    if (IS_ALIGNED(width, 8)) {
563      NV12ToARGBRow = NV12ToARGBRow_NEON;
564    }
565  }
566#endif
567
568  for (int y = 0; y < height; ++y) {
569    NV12ToARGBRow(src_y, src_uv, dst_argb, width);
570    dst_argb += dst_stride_argb;
571    src_y += src_stride_y;
572    if (y & 1) {
573      src_uv += src_stride_uv;
574    }
575  }
576  return 0;
577}
578
579// Convert NV21 to ARGB.
580LIBYUV_API
581int NV21ToARGB(const uint8* src_y, int src_stride_y,
582               const uint8* src_uv, int src_stride_uv,
583               uint8* dst_argb, int dst_stride_argb,
584               int width, int height) {
585  if (!src_y || !src_uv || !dst_argb ||
586      width <= 0 || height == 0) {
587    return -1;
588  }
589  // Negative height means invert the image.
590  if (height < 0) {
591    height = -height;
592    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
593    dst_stride_argb = -dst_stride_argb;
594  }
595  void (*NV21ToARGBRow)(const uint8* y_buf,
596                        const uint8* uv_buf,
597                        uint8* rgb_buf,
598                        int width) = NV21ToARGBRow_C;
599#if defined(HAS_NV21TOARGBROW_SSSE3)
600  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
601    NV21ToARGBRow = NV21ToARGBRow_Any_SSSE3;
602    if (IS_ALIGNED(width, 8)) {
603      NV21ToARGBRow = NV21ToARGBRow_Unaligned_SSSE3;
604      if (IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
605        NV21ToARGBRow = NV21ToARGBRow_SSSE3;
606      }
607    }
608  }
609#endif
610#if defined(HAS_NV21TOARGBROW_NEON)
611  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
612    NV21ToARGBRow = NV21ToARGBRow_Any_NEON;
613    if (IS_ALIGNED(width, 8)) {
614      NV21ToARGBRow = NV21ToARGBRow_NEON;
615    }
616  }
617#endif
618
619  for (int y = 0; y < height; ++y) {
620    NV21ToARGBRow(src_y, src_uv, dst_argb, width);
621    dst_argb += dst_stride_argb;
622    src_y += src_stride_y;
623    if (y & 1) {
624      src_uv += src_stride_uv;
625    }
626  }
627  return 0;
628}
629
630// Convert M420 to ARGB.
631LIBYUV_API
632int M420ToARGB(const uint8* src_m420, int src_stride_m420,
633               uint8* dst_argb, int dst_stride_argb,
634               int width, int height) {
635  if (!src_m420 || !dst_argb ||
636      width <= 0 || height == 0) {
637    return -1;
638  }
639  // Negative height means invert the image.
640  if (height < 0) {
641    height = -height;
642    dst_argb = dst_argb + (height - 1) * dst_stride_argb;
643    dst_stride_argb = -dst_stride_argb;
644  }
645  void (*NV12ToARGBRow)(const uint8* y_buf,
646                        const uint8* uv_buf,
647                        uint8* rgb_buf,
648                        int width) = NV12ToARGBRow_C;
649#if defined(HAS_NV12TOARGBROW_SSSE3)
650  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
651    NV12ToARGBRow = NV12ToARGBRow_Any_SSSE3;
652    if (IS_ALIGNED(width, 8)) {
653      NV12ToARGBRow = NV12ToARGBRow_Unaligned_SSSE3;
654      if (IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
655        NV12ToARGBRow = NV12ToARGBRow_SSSE3;
656      }
657    }
658  }
659#endif
660
661  for (int y = 0; y < height - 1; y += 2) {
662    NV12ToARGBRow(src_m420, src_m420 + src_stride_m420 * 2, dst_argb, width);
663    NV12ToARGBRow(src_m420 + src_stride_m420, src_m420 + src_stride_m420 * 2,
664                  dst_argb + dst_stride_argb, width);
665    dst_argb += dst_stride_argb * 2;
666    src_m420 += src_stride_m420 * 3;
667  }
668  if (height & 1) {
669    NV12ToARGBRow(src_m420, src_m420 + src_stride_m420 * 2, dst_argb, width);
670  }
671  return 0;
672}
673
674// Convert YUY2 to ARGB.
675LIBYUV_API
676int YUY2ToARGB(const uint8* src_yuy2, int src_stride_yuy2,
677               uint8* dst_argb, int dst_stride_argb,
678               int width, int height) {
679  if (!src_yuy2 || !dst_argb ||
680      width <= 0 || height == 0) {
681    return -1;
682  }
683  // Negative height means invert the image.
684  if (height < 0) {
685    height = -height;
686    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
687    src_stride_yuy2 = -src_stride_yuy2;
688  }
689  void (*YUY2ToUV422Row)(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v,
690      int pix) = YUY2ToUV422Row_C;
691  void (*YUY2ToYRow)(const uint8* src_yuy2,
692                     uint8* dst_y, int pix) = YUY2ToYRow_C;
693#if defined(HAS_YUY2TOYROW_SSE2)
694  if (TestCpuFlag(kCpuHasSSE2)) {
695    if (width > 16) {
696      YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
697      YUY2ToYRow = YUY2ToYRow_Any_SSE2;
698    }
699    if (IS_ALIGNED(width, 16)) {
700      YUY2ToUV422Row = YUY2ToUV422Row_Unaligned_SSE2;
701      YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
702      if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
703        YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
704        YUY2ToYRow = YUY2ToYRow_SSE2;
705      }
706    }
707  }
708#elif defined(HAS_YUY2TOYROW_NEON)
709  if (TestCpuFlag(kCpuHasNEON)) {
710    if (width > 8) {
711      YUY2ToYRow = YUY2ToYRow_Any_NEON;
712      if (width > 16) {
713        YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
714      }
715    }
716    if (IS_ALIGNED(width, 8)) {
717      YUY2ToYRow = YUY2ToYRow_NEON;
718      if (IS_ALIGNED(width, 16)) {
719        YUY2ToUV422Row = YUY2ToUV422Row_NEON;
720      }
721    }
722  }
723#endif
724
725  void (*I422ToARGBRow)(const uint8* y_buf,
726                        const uint8* u_buf,
727                        const uint8* v_buf,
728                        uint8* argb_buf,
729                        int width) = I422ToARGBRow_C;
730#if defined(HAS_I422TOARGBROW_NEON)
731  if (TestCpuFlag(kCpuHasNEON)) {
732    I422ToARGBRow = I422ToARGBRow_Any_NEON;
733    if (IS_ALIGNED(width, 16)) {
734      I422ToARGBRow = I422ToARGBRow_NEON;
735    }
736  }
737#elif defined(HAS_I422TOARGBROW_SSSE3)
738  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
739    I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
740    if (IS_ALIGNED(width, 8) &&
741        IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
742      I422ToARGBRow = I422ToARGBRow_SSSE3;
743    }
744  }
745#endif
746
747  SIMD_ALIGNED(uint8 rowy[kMaxStride]);
748  SIMD_ALIGNED(uint8 rowu[kMaxStride]);
749  SIMD_ALIGNED(uint8 rowv[kMaxStride]);
750
751  for (int y = 0; y < height; ++y) {
752    YUY2ToUV422Row(src_yuy2, rowu, rowv, width);
753    YUY2ToYRow(src_yuy2, rowy, width);
754    I422ToARGBRow(rowy, rowu, rowv, dst_argb, width);
755    src_yuy2 += src_stride_yuy2;
756    dst_argb += dst_stride_argb;
757  }
758  return 0;
759}
760
761// Convert UYVY to ARGB.
762LIBYUV_API
763int UYVYToARGB(const uint8* src_uyvy, int src_stride_uyvy,
764               uint8* dst_argb, int dst_stride_argb,
765               int width, int height) {
766  if (!src_uyvy || !dst_argb ||
767      width <= 0 || height == 0) {
768    return -1;
769  }
770  // Negative height means invert the image.
771  if (height < 0) {
772    height = -height;
773    src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
774    src_stride_uyvy = -src_stride_uyvy;
775  }
776  void (*UYVYToUV422Row)(const uint8* src_uyvy, uint8* dst_u, uint8* dst_v,
777      int pix) = UYVYToUV422Row_C;
778  void (*UYVYToYRow)(const uint8* src_uyvy,
779                     uint8* dst_y, int pix) = UYVYToYRow_C;
780#if defined(HAS_UYVYTOYROW_SSE2)
781  if (TestCpuFlag(kCpuHasSSE2)) {
782    if (width > 16) {
783      UYVYToUV422Row = UYVYToUV422Row_Any_SSE2;
784      UYVYToYRow = UYVYToYRow_Any_SSE2;
785    }
786    if (IS_ALIGNED(width, 16)) {
787      UYVYToUV422Row = UYVYToUV422Row_Unaligned_SSE2;
788      UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
789      if (IS_ALIGNED(src_uyvy, 16) && IS_ALIGNED(src_stride_uyvy, 16)) {
790        UYVYToUV422Row = UYVYToUV422Row_SSE2;
791        UYVYToYRow = UYVYToYRow_SSE2;
792      }
793    }
794  }
795#endif
796  void (*I422ToARGBRow)(const uint8* y_buf,
797                        const uint8* u_buf,
798                        const uint8* v_buf,
799                        uint8* argb_buf,
800                        int width) = I422ToARGBRow_C;
801#if defined(HAS_I422TOARGBROW_NEON)
802  if (TestCpuFlag(kCpuHasNEON)) {
803    I422ToARGBRow = I422ToARGBRow_Any_NEON;
804    if (IS_ALIGNED(width, 16)) {
805      I422ToARGBRow = I422ToARGBRow_NEON;
806    }
807  }
808#elif defined(HAS_I422TOARGBROW_SSSE3)
809  if (TestCpuFlag(kCpuHasSSSE3) && width >= 8) {
810    I422ToARGBRow = I422ToARGBRow_Any_SSSE3;
811    if (IS_ALIGNED(width, 8) &&
812        IS_ALIGNED(dst_argb, 16) && IS_ALIGNED(dst_stride_argb, 16)) {
813      I422ToARGBRow = I422ToARGBRow_SSSE3;
814    }
815  }
816#endif
817
818  SIMD_ALIGNED(uint8 rowy[kMaxStride]);
819  SIMD_ALIGNED(uint8 rowu[kMaxStride]);
820  SIMD_ALIGNED(uint8 rowv[kMaxStride]);
821
822  for (int y = 0; y < height; ++y) {
823    UYVYToUV422Row(src_uyvy, rowu, rowv, width);
824    UYVYToYRow(src_uyvy, rowy, width);
825    I422ToARGBRow(rowy, rowu, rowv, dst_argb, width);
826    src_uyvy += src_stride_uyvy;
827    dst_argb += dst_stride_argb;
828  }
829  return 0;
830}
831
832#ifdef HAVE_JPEG
833struct ARGBBuffers {
834  uint8* argb;
835  int argb_stride;
836  int w;
837  int h;
838};
839
840static void JpegI420ToARGB(void* opaque,
841                         const uint8* const* data,
842                         const int* strides,
843                         int rows) {
844  ARGBBuffers* dest = static_cast<ARGBBuffers*>(opaque);
845  I420ToARGB(data[0], strides[0],
846             data[1], strides[1],
847             data[2], strides[2],
848             dest->argb, dest->argb_stride,
849             dest->w, rows);
850  dest->argb += rows * dest->argb_stride;
851  dest->h -= rows;
852}
853
854static void JpegI422ToARGB(void* opaque,
855                           const uint8* const* data,
856                           const int* strides,
857                           int rows) {
858  ARGBBuffers* dest = static_cast<ARGBBuffers*>(opaque);
859  I422ToARGB(data[0], strides[0],
860             data[1], strides[1],
861             data[2], strides[2],
862             dest->argb, dest->argb_stride,
863             dest->w, rows);
864  dest->argb += rows * dest->argb_stride;
865  dest->h -= rows;
866}
867
868static void JpegI444ToARGB(void* opaque,
869                           const uint8* const* data,
870                           const int* strides,
871                           int rows) {
872  ARGBBuffers* dest = static_cast<ARGBBuffers*>(opaque);
873  I444ToARGB(data[0], strides[0],
874             data[1], strides[1],
875             data[2], strides[2],
876             dest->argb, dest->argb_stride,
877             dest->w, rows);
878  dest->argb += rows * dest->argb_stride;
879  dest->h -= rows;
880}
881
882static void JpegI411ToARGB(void* opaque,
883                           const uint8* const* data,
884                           const int* strides,
885                           int rows) {
886  ARGBBuffers* dest = static_cast<ARGBBuffers*>(opaque);
887  I411ToARGB(data[0], strides[0],
888             data[1], strides[1],
889             data[2], strides[2],
890             dest->argb, dest->argb_stride,
891             dest->w, rows);
892  dest->argb += rows * dest->argb_stride;
893  dest->h -= rows;
894}
895
896static void JpegI400ToARGB(void* opaque,
897                           const uint8* const* data,
898                           const int* strides,
899                           int rows) {
900  ARGBBuffers* dest = static_cast<ARGBBuffers*>(opaque);
901  I400ToARGB(data[0], strides[0],
902             dest->argb, dest->argb_stride,
903             dest->w, rows);
904  dest->argb += rows * dest->argb_stride;
905  dest->h -= rows;
906}
907
908// MJPG (Motion JPeg) to ARGB
909// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
910LIBYUV_API
911int MJPGToARGB(const uint8* sample,
912               size_t sample_size,
913               uint8* argb, int argb_stride,
914               int w, int h,
915               int dw, int dh) {
916  if (sample_size == kUnknownDataSize) {
917    // ERROR: MJPEG frame size unknown
918    return -1;
919  }
920
921  // TODO(fbarchard): Port to C
922  MJpegDecoder mjpeg_decoder;
923  bool ret = mjpeg_decoder.LoadFrame(sample, sample_size);
924  if (ret && (mjpeg_decoder.GetWidth() != w ||
925              mjpeg_decoder.GetHeight() != h)) {
926    // ERROR: MJPEG frame has unexpected dimensions
927    mjpeg_decoder.UnloadFrame();
928    return 1;  // runtime failure
929  }
930  if (ret) {
931    ARGBBuffers bufs = { argb, argb_stride, dw, dh };
932    // YUV420
933    if (mjpeg_decoder.GetColorSpace() ==
934            MJpegDecoder::kColorSpaceYCbCr &&
935        mjpeg_decoder.GetNumComponents() == 3 &&
936        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
937        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
938        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
939        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
940        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
941        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
942      ret = mjpeg_decoder.DecodeToCallback(&JpegI420ToARGB, &bufs, dw, dh);
943    // YUV422
944    } else if (mjpeg_decoder.GetColorSpace() ==
945                   MJpegDecoder::kColorSpaceYCbCr &&
946               mjpeg_decoder.GetNumComponents() == 3 &&
947               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
948               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
949               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
950               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
951               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
952               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
953      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToARGB, &bufs, dw, dh);
954    // YUV444
955    } else if (mjpeg_decoder.GetColorSpace() ==
956                   MJpegDecoder::kColorSpaceYCbCr &&
957               mjpeg_decoder.GetNumComponents() == 3 &&
958               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
959               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
960               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
961               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
962               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
963               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
964      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToARGB, &bufs, dw, dh);
965    // YUV411
966    } else if (mjpeg_decoder.GetColorSpace() ==
967                   MJpegDecoder::kColorSpaceYCbCr &&
968               mjpeg_decoder.GetNumComponents() == 3 &&
969               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
970               mjpeg_decoder.GetHorizSampFactor(0) == 4 &&
971               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
972               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
973               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
974               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
975      ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToARGB, &bufs, dw, dh);
976    // YUV400
977    } else if (mjpeg_decoder.GetColorSpace() ==
978                   MJpegDecoder::kColorSpaceGrayscale &&
979               mjpeg_decoder.GetNumComponents() == 1 &&
980               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
981               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
982      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToARGB, &bufs, dw, dh);
983    } else {
984      // TODO(fbarchard): Implement conversion for any other colorspace/sample
985      // factors that occur in practice. 411 is supported by libjpeg
986      // ERROR: Unable to convert MJPEG frame because format is not supported
987      mjpeg_decoder.UnloadFrame();
988      return 1;
989    }
990  }
991  return 0;
992}
993#endif
994
995// Convert camera sample to I420 with cropping, rotation and vertical flip.
996// src_width is used for source stride computation
997// src_height is used to compute location of planes, and indicate inversion
998// sample_size is measured in bytes and is the size of the frame.
999//   With MJPEG it is the compressed size of the frame.
1000LIBYUV_API
1001int ConvertToARGB(const uint8* sample, size_t sample_size,
1002                  uint8* dst_argb, int argb_stride,
1003                  int crop_x, int crop_y,
1004                  int src_width, int src_height,
1005                  int dst_width, int dst_height,
1006                  RotationMode rotation,
1007                  uint32 format) {
1008  if (dst_argb == NULL || sample == NULL ||
1009      src_width <= 0 || dst_width <= 0 ||
1010      src_height == 0 || dst_height == 0) {
1011    return -1;
1012  }
1013  int aligned_src_width = (src_width + 1) & ~1;
1014  const uint8* src;
1015  const uint8* src_uv;
1016  int abs_src_height = (src_height < 0) ? -src_height : src_height;
1017  int inv_dst_height = (dst_height < 0) ? -dst_height : dst_height;
1018  if (src_height < 0) {
1019    inv_dst_height = -inv_dst_height;
1020  }
1021  int r = 0;
1022
1023  // One pass rotation is available for some formats. For the rest, convert
1024  // to I420 (with optional vertical flipping) into a temporary I420 buffer,
1025  // and then rotate the I420 to the final destination buffer.
1026  // For in-place conversion, if destination dst_argb is same as source sample,
1027  // also enable temporary buffer.
1028  bool need_buf = (rotation && format != FOURCC_ARGB) || dst_argb == sample;
1029  uint8* tmp_argb = dst_argb;
1030  int tmp_argb_stride = argb_stride;
1031  uint8* buf = NULL;
1032  int abs_dst_height = (dst_height < 0) ? -dst_height : dst_height;
1033  if (need_buf) {
1034    int argb_size = dst_width * abs_dst_height * 4;
1035    buf = new uint8[argb_size];
1036    if (!buf) {
1037      return 1;  // Out of memory runtime error.
1038    }
1039    dst_argb = buf;
1040    argb_stride = dst_width;
1041  }
1042
1043  switch (format) {
1044    // Single plane formats
1045    case FOURCC_YUY2:
1046      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
1047      r = YUY2ToARGB(src, aligned_src_width * 2,
1048                     dst_argb, argb_stride,
1049                     dst_width, inv_dst_height);
1050      break;
1051    case FOURCC_UYVY:
1052      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
1053      r = UYVYToARGB(src, aligned_src_width * 2,
1054                     dst_argb, argb_stride,
1055                     dst_width, inv_dst_height);
1056      break;
1057//    case FOURCC_V210:
1058      // stride is multiple of 48 pixels (128 bytes).
1059      // pixels come in groups of 6 = 16 bytes
1060//      src = sample + (aligned_src_width + 47) / 48 * 128 * crop_y +
1061//            crop_x / 6 * 16;
1062//      r = V210ToARGB(src, (aligned_src_width + 47) / 48 * 128,
1063//                     dst_argb, argb_stride,
1064//                     dst_width, inv_dst_height);
1065//      break;
1066    case FOURCC_24BG:
1067      src = sample + (src_width * crop_y + crop_x) * 3;
1068      r = RGB24ToARGB(src, src_width * 3,
1069                      dst_argb, argb_stride,
1070                      dst_width, inv_dst_height);
1071      break;
1072    case FOURCC_RAW:
1073      src = sample + (src_width * crop_y + crop_x) * 3;
1074      r = RAWToARGB(src, src_width * 3,
1075                    dst_argb, argb_stride,
1076                    dst_width, inv_dst_height);
1077      break;
1078    case FOURCC_ARGB:
1079      src = sample + (src_width * crop_y + crop_x) * 4;
1080      r = ARGBToARGB(src, src_width * 4,
1081                     dst_argb, argb_stride,
1082                     dst_width, inv_dst_height);
1083      break;
1084    case FOURCC_BGRA:
1085      src = sample + (src_width * crop_y + crop_x) * 4;
1086      r = BGRAToARGB(src, src_width * 4,
1087                     dst_argb, argb_stride,
1088                     dst_width, inv_dst_height);
1089      break;
1090    case FOURCC_ABGR:
1091      src = sample + (src_width * crop_y + crop_x) * 4;
1092      r = ABGRToARGB(src, src_width * 4,
1093                     dst_argb, argb_stride,
1094                     dst_width, inv_dst_height);
1095      break;
1096    case FOURCC_RGBA:
1097      src = sample + (src_width * crop_y + crop_x) * 4;
1098      r = RGBAToARGB(src, src_width * 4,
1099                     dst_argb, argb_stride,
1100                     dst_width, inv_dst_height);
1101      break;
1102    case FOURCC_RGBP:
1103      src = sample + (src_width * crop_y + crop_x) * 2;
1104      r = RGB565ToARGB(src, src_width * 2,
1105                       dst_argb, argb_stride,
1106                       dst_width, inv_dst_height);
1107      break;
1108    case FOURCC_RGBO:
1109      src = sample + (src_width * crop_y + crop_x) * 2;
1110      r = ARGB1555ToARGB(src, src_width * 2,
1111                         dst_argb, argb_stride,
1112                         dst_width, inv_dst_height);
1113      break;
1114    case FOURCC_R444:
1115      src = sample + (src_width * crop_y + crop_x) * 2;
1116      r = ARGB4444ToARGB(src, src_width * 2,
1117                         dst_argb, argb_stride,
1118                         dst_width, inv_dst_height);
1119      break;
1120    // TODO(fbarchard): Support cropping Bayer by odd numbers
1121    // by adjusting fourcc.
1122    case FOURCC_BGGR:
1123      src = sample + (src_width * crop_y + crop_x);
1124      r = BayerBGGRToARGB(src, src_width,
1125                          dst_argb, argb_stride,
1126                          dst_width, inv_dst_height);
1127      break;
1128
1129    case FOURCC_GBRG:
1130      src = sample + (src_width * crop_y + crop_x);
1131      r = BayerGBRGToARGB(src, src_width,
1132                          dst_argb, argb_stride,
1133                          dst_width, inv_dst_height);
1134      break;
1135
1136    case FOURCC_GRBG:
1137      src = sample + (src_width * crop_y + crop_x);
1138      r = BayerGRBGToARGB(src, src_width,
1139                          dst_argb, argb_stride,
1140                          dst_width, inv_dst_height);
1141      break;
1142
1143    case FOURCC_RGGB:
1144      src = sample + (src_width * crop_y + crop_x);
1145      r = BayerRGGBToARGB(src, src_width,
1146                          dst_argb, argb_stride,
1147                          dst_width, inv_dst_height);
1148      break;
1149
1150    case FOURCC_I400:
1151      src = sample + src_width * crop_y + crop_x;
1152      r = I400ToARGB(src, src_width,
1153                     dst_argb, argb_stride,
1154                     dst_width, inv_dst_height);
1155      break;
1156
1157    // Biplanar formats
1158    case FOURCC_NV12:
1159      src = sample + (src_width * crop_y + crop_x);
1160      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
1161      r = NV12ToARGB(src, src_width,
1162                     src_uv, aligned_src_width,
1163                     dst_argb, argb_stride,
1164                     dst_width, inv_dst_height);
1165      break;
1166    case FOURCC_NV21:
1167      src = sample + (src_width * crop_y + crop_x);
1168      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
1169      // Call NV12 but with u and v parameters swapped.
1170      r = NV21ToARGB(src, src_width,
1171                     src_uv, aligned_src_width,
1172                     dst_argb, argb_stride,
1173                     dst_width, inv_dst_height);
1174      break;
1175    case FOURCC_M420:
1176      src = sample + (src_width * crop_y) * 12 / 8 + crop_x;
1177      r = M420ToARGB(src, src_width,
1178                     dst_argb, argb_stride,
1179                     dst_width, inv_dst_height);
1180      break;
1181//    case FOURCC_Q420:
1182//      src = sample + (src_width + aligned_src_width * 2) * crop_y + crop_x;
1183//      src_uv = sample + (src_width + aligned_src_width * 2) * crop_y +
1184//               src_width + crop_x * 2;
1185//      r = Q420ToARGB(src, src_width * 3,
1186//                    src_uv, src_width * 3,
1187//                    dst_argb, argb_stride,
1188//                    dst_width, inv_dst_height);
1189//      break;
1190    // Triplanar formats
1191    case FOURCC_I420:
1192    case FOURCC_YU12:
1193    case FOURCC_YV12: {
1194      const uint8* src_y = sample + (src_width * crop_y + crop_x);
1195      const uint8* src_u;
1196      const uint8* src_v;
1197      int halfwidth = (src_width + 1) / 2;
1198      int halfheight = (abs_src_height + 1) / 2;
1199      if (format == FOURCC_YV12) {
1200        src_v = sample + src_width * abs_src_height +
1201            (halfwidth * crop_y + crop_x) / 2;
1202        src_u = sample + src_width * abs_src_height +
1203            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
1204      } else {
1205        src_u = sample + src_width * abs_src_height +
1206            (halfwidth * crop_y + crop_x) / 2;
1207        src_v = sample + src_width * abs_src_height +
1208            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
1209      }
1210      r = I420ToARGB(src_y, src_width,
1211                     src_u, halfwidth,
1212                     src_v, halfwidth,
1213                     dst_argb, argb_stride,
1214                     dst_width, inv_dst_height);
1215      break;
1216    }
1217    case FOURCC_I422:
1218    case FOURCC_YV16: {
1219      const uint8* src_y = sample + src_width * crop_y + crop_x;
1220      const uint8* src_u;
1221      const uint8* src_v;
1222      int halfwidth = (src_width + 1) / 2;
1223      if (format == FOURCC_YV16) {
1224        src_v = sample + src_width * abs_src_height +
1225            halfwidth * crop_y + crop_x / 2;
1226        src_u = sample + src_width * abs_src_height +
1227            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
1228      } else {
1229        src_u = sample + src_width * abs_src_height +
1230            halfwidth * crop_y + crop_x / 2;
1231        src_v = sample + src_width * abs_src_height +
1232            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
1233      }
1234      r = I422ToARGB(src_y, src_width,
1235                     src_u, halfwidth,
1236                     src_v, halfwidth,
1237                     dst_argb, argb_stride,
1238                     dst_width, inv_dst_height);
1239      break;
1240    }
1241    case FOURCC_I444:
1242    case FOURCC_YV24: {
1243      const uint8* src_y = sample + src_width * crop_y + crop_x;
1244      const uint8* src_u;
1245      const uint8* src_v;
1246      if (format == FOURCC_YV24) {
1247        src_v = sample + src_width * (abs_src_height + crop_y) + crop_x;
1248        src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
1249      } else {
1250        src_u = sample + src_width * (abs_src_height + crop_y) + crop_x;
1251        src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
1252      }
1253      r = I444ToARGB(src_y, src_width,
1254                     src_u, src_width,
1255                     src_v, src_width,
1256                     dst_argb, argb_stride,
1257                     dst_width, inv_dst_height);
1258      break;
1259    }
1260    case FOURCC_I411: {
1261      int quarterwidth = (src_width + 3) / 4;
1262      const uint8* src_y = sample + src_width * crop_y + crop_x;
1263      const uint8* src_u = sample + src_width * abs_src_height +
1264          quarterwidth * crop_y + crop_x / 4;
1265      const uint8* src_v = sample + src_width * abs_src_height +
1266          quarterwidth * (abs_src_height + crop_y) + crop_x / 4;
1267      r = I411ToARGB(src_y, src_width,
1268                     src_u, quarterwidth,
1269                     src_v, quarterwidth,
1270                     dst_argb, argb_stride,
1271                     dst_width, inv_dst_height);
1272      break;
1273    }
1274#ifdef HAVE_JPEG
1275    case FOURCC_MJPG:
1276      r = MJPGToARGB(sample, sample_size,
1277                     dst_argb, argb_stride,
1278                     src_width, abs_src_height, dst_width, inv_dst_height);
1279      break;
1280#endif
1281    default:
1282      r = -1;  // unknown fourcc - return failure code.
1283  }
1284
1285  if (need_buf) {
1286    if (!r) {
1287      r = ARGBRotate(dst_argb, argb_stride,
1288                     tmp_argb, tmp_argb_stride,
1289                     dst_width, abs_dst_height, rotation);
1290    }
1291    delete buf;
1292  }
1293
1294  return r;
1295}
1296
1297#ifdef __cplusplus
1298}  // extern "C"
1299}  // namespace libyuv
1300#endif
1301