1/*
2 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "libyuv/convert.h"
12
13#include "libyuv/basic_types.h"
14#include "libyuv/cpu_id.h"
15#include "libyuv/format_conversion.h"
16#ifdef HAVE_JPEG
17#include "libyuv/mjpeg_decoder.h"
18#endif
19#include "libyuv/planar_functions.h"
20#include "libyuv/rotate.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 I420 with optional flipping
30LIBYUV_API
31int I420Copy(const uint8* src_y, int src_stride_y,
32             const uint8* src_u, int src_stride_u,
33             const uint8* src_v, int src_stride_v,
34             uint8* dst_y, int dst_stride_y,
35             uint8* dst_u, int dst_stride_u,
36             uint8* dst_v, int dst_stride_v,
37             int width, int height) {
38  if (!src_y || !src_u || !src_v ||
39      !dst_y || !dst_u || !dst_v ||
40      width <= 0 || height == 0) {
41    return -1;
42  }
43  // Negative height means invert the image.
44  if (height < 0) {
45    height = -height;
46    int halfheight = (height + 1) >> 1;
47    src_y = src_y + (height - 1) * src_stride_y;
48    src_u = src_u + (halfheight - 1) * src_stride_u;
49    src_v = src_v + (halfheight - 1) * src_stride_v;
50    src_stride_y = -src_stride_y;
51    src_stride_u = -src_stride_u;
52    src_stride_v = -src_stride_v;
53  }
54
55  int halfwidth = (width + 1) >> 1;
56  int halfheight = (height + 1) >> 1;
57  if (dst_y) {
58    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
59  }
60  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
61  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
62  return 0;
63}
64
65// Move to row_win etc.
66#if !defined(YUV_DISABLE_ASM) && defined(_M_IX86)
67#define HAS_HALFROW_SSE2
68__declspec(naked) __declspec(align(16))
69static void HalfRow_SSE2(const uint8* src_uv, int src_uv_stride,
70                         uint8* dst_uv, int pix) {
71  __asm {
72    push       edi
73    mov        eax, [esp + 4 + 4]    // src_uv
74    mov        edx, [esp + 4 + 8]    // src_uv_stride
75    mov        edi, [esp + 4 + 12]   // dst_v
76    mov        ecx, [esp + 4 + 16]   // pix
77    sub        edi, eax
78
79    align      16
80  convertloop:
81    movdqa     xmm0, [eax]
82    pavgb      xmm0, [eax + edx]
83    sub        ecx, 16
84    movdqa     [eax + edi], xmm0
85    lea        eax,  [eax + 16]
86    jg         convertloop
87    pop        edi
88    ret
89  }
90}
91
92#elif !defined(YUV_DISABLE_ASM) && (defined(__x86_64__) || defined(__i386__))
93#define HAS_HALFROW_SSE2
94static void HalfRow_SSE2(const uint8* src_uv, int src_uv_stride,
95                         uint8* dst_uv, int pix) {
96  asm volatile (
97  "sub        %0,%1                            \n"
98  ".p2align  4                                 \n"
99"1:                                            \n"
100  "movdqa     (%0),%%xmm0                      \n"
101  "pavgb      (%0,%3),%%xmm0                   \n"
102  "sub        $0x10,%2                         \n"
103  "movdqa     %%xmm0,(%0,%1)                   \n"
104  "lea        0x10(%0),%0                      \n"
105  "jg         1b                               \n"
106  : "+r"(src_uv),  // %0
107    "+r"(dst_uv),  // %1
108    "+r"(pix)      // %2
109  : "r"(static_cast<intptr_t>(src_uv_stride))  // %3
110  : "memory", "cc"
111#if defined(__SSE2__)
112    , "xmm0"
113#endif
114);
115}
116#endif
117
118static void HalfRow_C(const uint8* src_uv, int src_uv_stride,
119                      uint8* dst_uv, int pix) {
120  for (int x = 0; x < pix; ++x) {
121    dst_uv[x] = (src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1;
122  }
123}
124
125LIBYUV_API
126int I422ToI420(const uint8* src_y, int src_stride_y,
127               const uint8* src_u, int src_stride_u,
128               const uint8* src_v, int src_stride_v,
129               uint8* dst_y, int dst_stride_y,
130               uint8* dst_u, int dst_stride_u,
131               uint8* dst_v, int dst_stride_v,
132               int width, int height) {
133  if (!src_y || !src_u || !src_v ||
134      !dst_y || !dst_u || !dst_v ||
135      width <= 0 || height == 0) {
136    return -1;
137  }
138  // Negative height means invert the image.
139  if (height < 0) {
140    height = -height;
141    src_y = src_y + (height - 1) * src_stride_y;
142    src_u = src_u + (height - 1) * src_stride_u;
143    src_v = src_v + (height - 1) * src_stride_v;
144    src_stride_y = -src_stride_y;
145    src_stride_u = -src_stride_u;
146    src_stride_v = -src_stride_v;
147  }
148  int halfwidth = (width + 1) >> 1;
149  void (*HalfRow)(const uint8* src_uv, int src_uv_stride,
150                  uint8* dst_uv, int pix) = HalfRow_C;
151#if defined(HAS_HALFROW_SSE2)
152  if (TestCpuFlag(kCpuHasSSE2) &&
153      IS_ALIGNED(halfwidth, 16) &&
154      IS_ALIGNED(src_u, 16) && IS_ALIGNED(src_stride_u, 16) &&
155      IS_ALIGNED(src_v, 16) && IS_ALIGNED(src_stride_v, 16) &&
156      IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
157      IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
158    HalfRow = HalfRow_SSE2;
159  }
160#endif
161
162  // Copy Y plane
163  if (dst_y) {
164    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
165  }
166
167  // SubSample U plane.
168  int y;
169  for (y = 0; y < height - 1; y += 2) {
170    HalfRow(src_u, src_stride_u, dst_u, halfwidth);
171    src_u += src_stride_u * 2;
172    dst_u += dst_stride_u;
173  }
174  if (height & 1) {
175    HalfRow(src_u, 0, dst_u, halfwidth);
176  }
177
178  // SubSample V plane.
179  for (y = 0; y < height - 1; y += 2) {
180    HalfRow(src_v, src_stride_v, dst_v, halfwidth);
181    src_v += src_stride_v * 2;
182    dst_v += dst_stride_v;
183  }
184  if (height & 1) {
185    HalfRow(src_v, 0, dst_v, halfwidth);
186  }
187  return 0;
188}
189
190// Blends 32x2 pixels to 16x1
191// source in scale.cc
192#if !defined(YUV_DISABLE_ASM) && (defined(__ARM_NEON__) || defined(LIBYUV_NEON))
193#define HAS_SCALEROWDOWN2_NEON
194void ScaleRowDown2Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
195                           uint8* dst, int dst_width);
196#elif !defined(YUV_DISABLE_ASM) && \
197    (defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))
198
199void ScaleRowDown2Int_SSE2(const uint8* src_ptr, ptrdiff_t src_stride,
200                           uint8* dst_ptr, int dst_width);
201#endif
202void ScaleRowDown2Int_C(const uint8* src_ptr, ptrdiff_t src_stride,
203                        uint8* dst_ptr, int dst_width);
204
205LIBYUV_API
206int I444ToI420(const uint8* src_y, int src_stride_y,
207               const uint8* src_u, int src_stride_u,
208               const uint8* src_v, int src_stride_v,
209               uint8* dst_y, int dst_stride_y,
210               uint8* dst_u, int dst_stride_u,
211               uint8* dst_v, int dst_stride_v,
212               int width, int height) {
213  if (!src_y || !src_u || !src_v ||
214      !dst_y || !dst_u || !dst_v ||
215      width <= 0 || height == 0) {
216    return -1;
217  }
218  // Negative height means invert the image.
219  if (height < 0) {
220    height = -height;
221    src_y = src_y + (height - 1) * src_stride_y;
222    src_u = src_u + (height - 1) * src_stride_u;
223    src_v = src_v + (height - 1) * src_stride_v;
224    src_stride_y = -src_stride_y;
225    src_stride_u = -src_stride_u;
226    src_stride_v = -src_stride_v;
227  }
228  int halfwidth = (width + 1) >> 1;
229  void (*ScaleRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride,
230                        uint8* dst_ptr, int dst_width) = ScaleRowDown2Int_C;
231#if defined(HAS_SCALEROWDOWN2_NEON)
232  if (TestCpuFlag(kCpuHasNEON) &&
233      IS_ALIGNED(halfwidth, 16)) {
234    ScaleRowDown2 = ScaleRowDown2Int_NEON;
235  }
236#elif defined(HAS_SCALEROWDOWN2_SSE2)
237  if (TestCpuFlag(kCpuHasSSE2) &&
238      IS_ALIGNED(halfwidth, 16) &&
239      IS_ALIGNED(src_u, 16) && IS_ALIGNED(src_stride_u, 16) &&
240      IS_ALIGNED(src_v, 16) && IS_ALIGNED(src_stride_v, 16) &&
241      IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
242      IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
243    ScaleRowDown2 = ScaleRowDown2Int_SSE2;
244  }
245#endif
246
247  // Copy Y plane
248  if (dst_y) {
249    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
250  }
251
252  // SubSample U plane.
253  int y;
254  for (y = 0; y < height - 1; y += 2) {
255    ScaleRowDown2(src_u, src_stride_u, dst_u, halfwidth);
256    src_u += src_stride_u * 2;
257    dst_u += dst_stride_u;
258  }
259  if (height & 1) {
260    ScaleRowDown2(src_u, 0, dst_u, halfwidth);
261  }
262
263  // SubSample V plane.
264  for (y = 0; y < height - 1; y += 2) {
265    ScaleRowDown2(src_v, src_stride_v, dst_v, halfwidth);
266    src_v += src_stride_v * 2;
267    dst_v += dst_stride_v;
268  }
269  if (height & 1) {
270    ScaleRowDown2(src_v, 0, dst_v, halfwidth);
271  }
272  return 0;
273}
274
275// use Bilinear for upsampling chroma
276void ScalePlaneBilinear(int src_width, int src_height,
277                        int dst_width, int dst_height,
278                        int src_stride, int dst_stride,
279                        const uint8* src_ptr, uint8* dst_ptr);
280
281// 411 chroma is 1/4 width, 1x height
282// 420 chroma is 1/2 width, 1/2 height
283LIBYUV_API
284int I411ToI420(const uint8* src_y, int src_stride_y,
285               const uint8* src_u, int src_stride_u,
286               const uint8* src_v, int src_stride_v,
287               uint8* dst_y, int dst_stride_y,
288               uint8* dst_u, int dst_stride_u,
289               uint8* dst_v, int dst_stride_v,
290               int width, int height) {
291  if (!src_y || !src_u || !src_v ||
292      !dst_y || !dst_u || !dst_v ||
293      width <= 0 || height == 0) {
294    return -1;
295  }
296  // Negative height means invert the image.
297  if (height < 0) {
298    height = -height;
299    dst_y = dst_y + (height - 1) * dst_stride_y;
300    dst_u = dst_u + (height - 1) * dst_stride_u;
301    dst_v = dst_v + (height - 1) * dst_stride_v;
302    dst_stride_y = -dst_stride_y;
303    dst_stride_u = -dst_stride_u;
304    dst_stride_v = -dst_stride_v;
305  }
306
307  // Copy Y plane
308  if (dst_y) {
309    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
310  }
311
312  int halfwidth = (width + 1) >> 1;
313  int halfheight = (height + 1) >> 1;
314  int quarterwidth = (width + 3) >> 2;
315
316  // Resample U plane.
317  ScalePlaneBilinear(quarterwidth, height,  // from 1/4 width, 1x height
318                     halfwidth, halfheight,  // to 1/2 width, 1/2 height
319                     src_stride_u,
320                     dst_stride_u,
321                     src_u, dst_u);
322
323  // Resample V plane.
324  ScalePlaneBilinear(quarterwidth, height,  // from 1/4 width, 1x height
325                     halfwidth, halfheight,  // to 1/2 width, 1/2 height
326                     src_stride_v,
327                     dst_stride_v,
328                     src_v, dst_v);
329  return 0;
330}
331
332// I400 is greyscale typically used in MJPG
333LIBYUV_API
334int I400ToI420(const uint8* src_y, int src_stride_y,
335               uint8* dst_y, int dst_stride_y,
336               uint8* dst_u, int dst_stride_u,
337               uint8* dst_v, int dst_stride_v,
338               int width, int height) {
339  if (!src_y || !dst_y || !dst_u || !dst_v ||
340      width <= 0 || height == 0) {
341    return -1;
342  }
343  // Negative height means invert the image.
344  if (height < 0) {
345    height = -height;
346    src_y = src_y + (height - 1) * src_stride_y;
347    src_stride_y = -src_stride_y;
348  }
349  int halfwidth = (width + 1) >> 1;
350  int halfheight = (height + 1) >> 1;
351  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
352  SetPlane(dst_u, dst_stride_u, halfwidth, halfheight, 128);
353  SetPlane(dst_v, dst_stride_v, halfwidth, halfheight, 128);
354  return 0;
355}
356
357static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1,
358                       uint8* dst, int dst_stride_frame,
359                       int width, int height) {
360  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
361#if defined(HAS_COPYROW_NEON)
362  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 64)) {
363    CopyRow = CopyRow_NEON;
364  }
365#elif defined(HAS_COPYROW_X86)
366  if (IS_ALIGNED(width, 4)) {
367    CopyRow = CopyRow_X86;
368#if defined(HAS_COPYROW_SSE2)
369    if (TestCpuFlag(kCpuHasSSE2) &&
370        IS_ALIGNED(width, 32) && IS_ALIGNED(src, 16) &&
371        IS_ALIGNED(src_stride_0, 16) && IS_ALIGNED(src_stride_1, 16) &&
372        IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride_frame, 16)) {
373      CopyRow = CopyRow_SSE2;
374    }
375#endif
376  }
377#endif
378
379  // Copy plane
380  for (int y = 0; y < height - 1; y += 2) {
381    CopyRow(src, dst, width);
382    CopyRow(src + src_stride_0, dst + dst_stride_frame, width);
383    src += src_stride_0 + src_stride_1;
384    dst += dst_stride_frame * 2;
385  }
386  if (height & 1) {
387    CopyRow(src, dst, width);
388  }
389}
390
391// Support converting from FOURCC_M420
392// Useful for bandwidth constrained transports like USB 1.0 and 2.0 and for
393// easy conversion to I420.
394// M420 format description:
395// M420 is row biplanar 420: 2 rows of Y and 1 row of UV.
396// Chroma is half width / half height. (420)
397// src_stride_m420 is row planar. Normally this will be the width in pixels.
398//   The UV plane is half width, but 2 values, so src_stride_m420 applies to
399//   this as well as the two Y planes.
400static int X420ToI420(const uint8* src_y,
401                      int src_stride_y0, int src_stride_y1,
402                      const uint8* src_uv, int src_stride_uv,
403                      uint8* dst_y, int dst_stride_y,
404                      uint8* dst_u, int dst_stride_u,
405                      uint8* dst_v, int dst_stride_v,
406                      int width, int height) {
407  if (!src_y || !src_uv ||
408      !dst_y || !dst_u || !dst_v ||
409      width <= 0 || height == 0) {
410    return -1;
411  }
412  // Negative height means invert the image.
413  if (height < 0) {
414    height = -height;
415    int halfheight = (height + 1) >> 1;
416    dst_y = dst_y + (height - 1) * dst_stride_y;
417    dst_u = dst_u + (halfheight - 1) * dst_stride_u;
418    dst_v = dst_v + (halfheight - 1) * dst_stride_v;
419    dst_stride_y = -dst_stride_y;
420    dst_stride_u = -dst_stride_u;
421    dst_stride_v = -dst_stride_v;
422  }
423
424  int halfwidth = (width + 1) >> 1;
425  void (*SplitUV)(const uint8* src_uv, uint8* dst_u, uint8* dst_v, int pix) =
426      SplitUV_C;
427#if defined(HAS_SPLITUV_NEON)
428  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(halfwidth, 16)) {
429    SplitUV = SplitUV_NEON;
430  }
431#elif defined(HAS_SPLITUV_SSE2)
432  if (TestCpuFlag(kCpuHasSSE2) &&
433      IS_ALIGNED(halfwidth, 16) &&
434      IS_ALIGNED(src_uv, 16) && IS_ALIGNED(src_stride_uv, 16) &&
435      IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
436      IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
437    SplitUV = SplitUV_SSE2;
438  }
439#endif
440
441  if (dst_y) {
442    CopyPlane2(src_y, src_stride_y0, src_stride_y1, dst_y, dst_stride_y,
443               width, height);
444  }
445
446  int halfheight = (height + 1) >> 1;
447  for (int y = 0; y < halfheight; ++y) {
448    // Copy a row of UV.
449    SplitUV(src_uv, dst_u, dst_v, halfwidth);
450    dst_u += dst_stride_u;
451    dst_v += dst_stride_v;
452    src_uv += src_stride_uv;
453  }
454  return 0;
455}
456
457// Convert NV12 to I420.
458LIBYUV_API
459int NV12ToI420(const uint8* src_y, int src_stride_y,
460               const uint8* src_uv, int src_stride_uv,
461               uint8* dst_y, int dst_stride_y,
462               uint8* dst_u, int dst_stride_u,
463               uint8* dst_v, int dst_stride_v,
464               int width, int height) {
465  return X420ToI420(src_y, src_stride_y, src_stride_y,
466                    src_uv, src_stride_uv,
467                    dst_y, dst_stride_y,
468                    dst_u, dst_stride_u,
469                    dst_v, dst_stride_v,
470                    width, height);
471}
472
473// Convert M420 to I420.
474LIBYUV_API
475int M420ToI420(const uint8* src_m420, int src_stride_m420,
476               uint8* dst_y, int dst_stride_y,
477               uint8* dst_u, int dst_stride_u,
478               uint8* dst_v, int dst_stride_v,
479               int width, int height) {
480  return X420ToI420(src_m420, src_stride_m420, src_stride_m420 * 2,
481                    src_m420 + src_stride_m420 * 2, src_stride_m420 * 3,
482                    dst_y, dst_stride_y,
483                    dst_u, dst_stride_u,
484                    dst_v, dst_stride_v,
485                    width, height);
486}
487
488// Convert Q420 to I420.
489// Format is rows of YY/YUYV
490LIBYUV_API
491int Q420ToI420(const uint8* src_y, int src_stride_y,
492               const uint8* src_yuy2, int src_stride_yuy2,
493               uint8* dst_y, int dst_stride_y,
494               uint8* dst_u, int dst_stride_u,
495               uint8* dst_v, int dst_stride_v,
496               int width, int height) {
497  if (!src_y || !src_yuy2 ||
498      !dst_y || !dst_u || !dst_v ||
499      width <= 0 || height == 0) {
500    return -1;
501  }
502  // Negative height means invert the image.
503  if (height < 0) {
504    height = -height;
505    int halfheight = (height + 1) >> 1;
506    dst_y = dst_y + (height - 1) * dst_stride_y;
507    dst_u = dst_u + (halfheight - 1) * dst_stride_u;
508    dst_v = dst_v + (halfheight - 1) * dst_stride_v;
509    dst_stride_y = -dst_stride_y;
510    dst_stride_u = -dst_stride_u;
511    dst_stride_v = -dst_stride_v;
512  }
513  // CopyRow for rows of just Y in Q420 copied to Y plane of I420.
514  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
515#if defined(HAS_COPYROW_NEON)
516  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 64)) {
517    CopyRow = CopyRow_NEON;
518  }
519#endif
520#if defined(HAS_COPYROW_X86)
521  if (IS_ALIGNED(width, 4)) {
522    CopyRow = CopyRow_X86;
523  }
524#endif
525#if defined(HAS_COPYROW_SSE2)
526  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) &&
527      IS_ALIGNED(src_y, 16) && IS_ALIGNED(src_stride_y, 16) &&
528      IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
529    CopyRow = CopyRow_SSE2;
530  }
531#endif
532
533  void (*YUY2ToUV422Row)(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v,
534      int pix) = YUY2ToUV422Row_C;
535  void (*YUY2ToYRow)(const uint8* src_yuy2, uint8* dst_y, int pix) =
536      YUY2ToYRow_C;
537#if defined(HAS_YUY2TOYROW_SSE2)
538  if (TestCpuFlag(kCpuHasSSE2)) {
539    if (width > 16) {
540      YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
541      YUY2ToYRow = YUY2ToYRow_Any_SSE2;
542    }
543    if (IS_ALIGNED(width, 16)) {
544      YUY2ToUV422Row = YUY2ToUV422Row_Unaligned_SSE2;
545      YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
546      if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
547        YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
548        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
549          YUY2ToYRow = YUY2ToYRow_SSE2;
550        }
551      }
552    }
553  }
554#elif defined(HAS_YUY2TOYROW_NEON)
555  if (TestCpuFlag(kCpuHasNEON)) {
556    if (width > 8) {
557      YUY2ToYRow = YUY2ToYRow_Any_NEON;
558      if (width > 16) {
559        YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
560      }
561    }
562    if (IS_ALIGNED(width, 16)) {
563      YUY2ToYRow = YUY2ToYRow_NEON;
564      YUY2ToUV422Row = YUY2ToUV422Row_NEON;
565    }
566  }
567#endif
568
569  for (int y = 0; y < height - 1; y += 2) {
570    CopyRow(src_y, dst_y, width);
571    src_y += src_stride_y;
572    dst_y += dst_stride_y;
573
574    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
575    YUY2ToYRow(src_yuy2, dst_y, width);
576    src_yuy2 += src_stride_yuy2;
577    dst_y += dst_stride_y;
578    dst_u += dst_stride_u;
579    dst_v += dst_stride_v;
580  }
581  if (height & 1) {
582    CopyRow(src_y, dst_y, width);
583    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
584  }
585  return 0;
586}
587
588// Test if over reading on source is safe.
589// TODO(fbarchard): Find more efficient solution to safely do odd sizes.
590// Macros to control read policy, from slowest to fastest:
591// READSAFE_NEVER - disables read ahead on systems with strict memory reads
592// READSAFE_ODDHEIGHT - last row of odd height done with C.
593//   This policy assumes that the caller handles the last row of an odd height
594//   image using C.
595// READSAFE_PAGE - enable read ahead within same page.
596//   A page is 4096 bytes. When reading ahead, if the last pixel is near the
597//   end the page, and a read spans the page into the next page, a memory
598//   exception can occur if that page has not been allocated, or is a guard
599//   page. This setting ensures the overread is within the same page.
600// READSAFE_ALWAYS - enables read ahead on systems without memory exceptions
601//   or where buffers are padded by 64 bytes.
602
603#if defined(HAS_RGB24TOARGBROW_SSSE3) || \
604    defined(HAS_RGB24TOARGBROW_SSSE3) || \
605    defined(HAS_RAWTOARGBROW_SSSE3) || \
606    defined(HAS_RGB565TOARGBROW_SSE2) || \
607    defined(HAS_ARGB1555TOARGBROW_SSE2) || \
608    defined(HAS_ARGB4444TOARGBROW_SSE2)
609
610#define READSAFE_ODDHEIGHT
611
612static bool TestReadSafe(const uint8* src_yuy2, int src_stride_yuy2,
613                        int width, int height, int bpp, int overread) {
614  if (width > kMaxStride) {
615    return false;
616  }
617#if defined(READSAFE_ALWAYS)
618  return true;
619#elif defined(READSAFE_NEVER)
620  return false;
621#elif defined(READSAFE_ODDHEIGHT)
622  if (!(width & 15) ||
623      (src_stride_yuy2 >= 0 && (height & 1) && width * bpp >= overread)) {
624    return true;
625  }
626  return false;
627#elif defined(READSAFE_PAGE)
628  if (src_stride_yuy2 >= 0) {
629    src_yuy2 += (height - 1) * src_stride_yuy2;
630  }
631  uintptr_t last_adr = (uintptr_t)(src_yuy2) + width * bpp - 1;
632  uintptr_t last_read_adr = last_adr + overread - 1;
633  if (((last_adr ^ last_read_adr) & ~4095) == 0) {
634    return true;
635  }
636  return false;
637#endif
638}
639#endif
640
641// Convert YUY2 to I420.
642LIBYUV_API
643int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
644               uint8* dst_y, int dst_stride_y,
645               uint8* dst_u, int dst_stride_u,
646               uint8* dst_v, int dst_stride_v,
647               int width, int height) {
648  // Negative height means invert the image.
649  if (height < 0) {
650    height = -height;
651    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
652    src_stride_yuy2 = -src_stride_yuy2;
653  }
654  void (*YUY2ToUVRow)(const uint8* src_yuy2, int src_stride_yuy2,
655                      uint8* dst_u, uint8* dst_v, int pix);
656  void (*YUY2ToYRow)(const uint8* src_yuy2,
657                     uint8* dst_y, int pix);
658  YUY2ToYRow = YUY2ToYRow_C;
659  YUY2ToUVRow = YUY2ToUVRow_C;
660#if defined(HAS_YUY2TOYROW_SSE2)
661  if (TestCpuFlag(kCpuHasSSE2)) {
662    if (width > 16) {
663      YUY2ToUVRow = YUY2ToUVRow_Any_SSE2;
664      YUY2ToYRow = YUY2ToYRow_Any_SSE2;
665    }
666    if (IS_ALIGNED(width, 16)) {
667      YUY2ToUVRow = YUY2ToUVRow_Unaligned_SSE2;
668      YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
669      if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
670        YUY2ToUVRow = YUY2ToUVRow_SSE2;
671        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
672          YUY2ToYRow = YUY2ToYRow_SSE2;
673        }
674      }
675    }
676  }
677#elif defined(HAS_YUY2TOYROW_NEON)
678  if (TestCpuFlag(kCpuHasNEON)) {
679    if (width > 8) {
680      YUY2ToYRow = YUY2ToYRow_Any_NEON;
681      if (width > 16) {
682        YUY2ToUVRow = YUY2ToUVRow_Any_NEON;
683      }
684    }
685    if (IS_ALIGNED(width, 16)) {
686      YUY2ToYRow = YUY2ToYRow_NEON;
687      YUY2ToUVRow = YUY2ToUVRow_NEON;
688    }
689  }
690#endif
691
692  for (int y = 0; y < height - 1; y += 2) {
693    YUY2ToUVRow(src_yuy2, src_stride_yuy2, dst_u, dst_v, width);
694    YUY2ToYRow(src_yuy2, dst_y, width);
695    YUY2ToYRow(src_yuy2 + src_stride_yuy2, dst_y + dst_stride_y, width);
696    src_yuy2 += src_stride_yuy2 * 2;
697    dst_y += dst_stride_y * 2;
698    dst_u += dst_stride_u;
699    dst_v += dst_stride_v;
700  }
701  if (height & 1) {
702    YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width);
703    YUY2ToYRow(src_yuy2, dst_y, width);
704  }
705  return 0;
706}
707
708// Convert UYVY to I420.
709LIBYUV_API
710int UYVYToI420(const uint8* src_uyvy, int src_stride_uyvy,
711               uint8* dst_y, int dst_stride_y,
712               uint8* dst_u, int dst_stride_u,
713               uint8* dst_v, int dst_stride_v,
714               int width, int height) {
715  // Negative height means invert the image.
716  if (height < 0) {
717    height = -height;
718    src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
719    src_stride_uyvy = -src_stride_uyvy;
720  }
721  void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy,
722                      uint8* dst_u, uint8* dst_v, int pix);
723  void (*UYVYToYRow)(const uint8* src_uyvy,
724                     uint8* dst_y, int pix);
725  UYVYToYRow = UYVYToYRow_C;
726  UYVYToUVRow = UYVYToUVRow_C;
727#if defined(HAS_UYVYTOYROW_SSE2)
728  if (TestCpuFlag(kCpuHasSSE2)) {
729    if (width > 16) {
730      UYVYToUVRow = UYVYToUVRow_Any_SSE2;
731      UYVYToYRow = UYVYToYRow_Any_SSE2;
732    }
733    if (IS_ALIGNED(width, 16)) {
734      UYVYToUVRow = UYVYToUVRow_Unaligned_SSE2;
735      UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
736      if (IS_ALIGNED(src_uyvy, 16) && IS_ALIGNED(src_stride_uyvy, 16)) {
737        UYVYToUVRow = UYVYToUVRow_SSE2;
738        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
739          UYVYToYRow = UYVYToYRow_SSE2;
740        }
741      }
742    }
743  }
744#elif defined(HAS_UYVYTOYROW_NEON)
745  if (TestCpuFlag(kCpuHasNEON)) {
746    if (width > 8) {
747      UYVYToYRow = UYVYToYRow_Any_NEON;
748      if (width > 16) {
749        UYVYToUVRow = UYVYToUVRow_Any_NEON;
750      }
751    }
752    if (IS_ALIGNED(width, 16)) {
753      UYVYToYRow = UYVYToYRow_NEON;
754      UYVYToUVRow = UYVYToUVRow_NEON;
755    }
756  }
757#endif
758
759  for (int y = 0; y < height - 1; y += 2) {
760    UYVYToUVRow(src_uyvy, src_stride_uyvy, dst_u, dst_v, width);
761    UYVYToYRow(src_uyvy, dst_y, width);
762    UYVYToYRow(src_uyvy + src_stride_uyvy, dst_y + dst_stride_y, width);
763    src_uyvy += src_stride_uyvy * 2;
764    dst_y += dst_stride_y * 2;
765    dst_u += dst_stride_u;
766    dst_v += dst_stride_v;
767  }
768  if (height & 1) {
769    UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width);
770    UYVYToYRow(src_uyvy, dst_y, width);
771  }
772  return 0;
773}
774
775// Visual C x86 or GCC little endian.
776#if defined(__x86_64__) || defined(_M_X64) || \
777  defined(__i386__) || defined(_M_IX86) || \
778  defined(__arm__) || defined(_M_ARM) || \
779  (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
780#define LIBYUV_LITTLE_ENDIAN
781#endif
782
783#ifdef LIBYUV_LITTLE_ENDIAN
784#define READWORD(p) (*reinterpret_cast<const uint32*>(p))
785#else
786static inline uint32 READWORD(const uint8* p) {
787  return static_cast<uint32>(p[0]) |
788      (static_cast<uint32>(p[1]) << 8) |
789      (static_cast<uint32>(p[2]) << 16) |
790      (static_cast<uint32>(p[3]) << 24);
791}
792#endif
793
794// Must be multiple of 6 pixels. Will over convert to handle remainder.
795// https://developer.apple.com/quicktime/icefloe/dispatch019.html#v210
796static void V210ToUYVYRow_C(const uint8* src_v210, uint8* dst_uyvy, int width) {
797  for (int x = 0; x < width; x += 6) {
798    uint32 w = READWORD(src_v210 + 0);
799    dst_uyvy[0] = (w >> 2) & 0xff;
800    dst_uyvy[1] = (w >> 12) & 0xff;
801    dst_uyvy[2] = (w >> 22) & 0xff;
802
803    w = READWORD(src_v210 + 4);
804    dst_uyvy[3] = (w >> 2) & 0xff;
805    dst_uyvy[4] = (w >> 12) & 0xff;
806    dst_uyvy[5] = (w >> 22) & 0xff;
807
808    w = READWORD(src_v210 + 8);
809    dst_uyvy[6] = (w >> 2) & 0xff;
810    dst_uyvy[7] = (w >> 12) & 0xff;
811    dst_uyvy[8] = (w >> 22) & 0xff;
812
813    w = READWORD(src_v210 + 12);
814    dst_uyvy[9] = (w >> 2) & 0xff;
815    dst_uyvy[10] = (w >> 12) & 0xff;
816    dst_uyvy[11] = (w >> 22) & 0xff;
817
818    src_v210 += 16;
819    dst_uyvy += 12;
820  }
821}
822
823// Convert V210 to I420.
824// V210 is 10 bit version of UYVY. 16 bytes to store 6 pixels.
825// With is multiple of 48.
826LIBYUV_API
827int V210ToI420(const uint8* src_v210, int src_stride_v210,
828               uint8* dst_y, int dst_stride_y,
829               uint8* dst_u, int dst_stride_u,
830               uint8* dst_v, int dst_stride_v,
831               int width, int height) {
832  if (width * 2 * 2 > kMaxStride) {  // 2 rows of UYVY are required.
833    return -1;
834  } else if (!src_v210 || !dst_y || !dst_u || !dst_v ||
835             width <= 0 || height == 0) {
836    return -1;
837  }
838  // Negative height means invert the image.
839  if (height < 0) {
840    height = -height;
841    src_v210 = src_v210 + (height - 1) * src_stride_v210;
842    src_stride_v210 = -src_stride_v210;
843  }
844  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
845  void (*V210ToUYVYRow)(const uint8* src_v210, uint8* dst_uyvy, int pix);
846  V210ToUYVYRow = V210ToUYVYRow_C;
847
848  void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy,
849                      uint8* dst_u, uint8* dst_v, int pix);
850  void (*UYVYToYRow)(const uint8* src_uyvy,
851                     uint8* dst_y, int pix);
852  UYVYToYRow = UYVYToYRow_C;
853  UYVYToUVRow = UYVYToUVRow_C;
854#if defined(HAS_UYVYTOYROW_SSE2)
855  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 16)) {
856    UYVYToUVRow = UYVYToUVRow_SSE2;
857    UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
858    if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
859      UYVYToYRow = UYVYToYRow_SSE2;
860    }
861  }
862#elif defined(HAS_UYVYTOYROW_NEON)
863  if (TestCpuFlag(kCpuHasNEON)) {
864    if (width > 8) {
865      UYVYToYRow = UYVYToYRow_Any_NEON;
866      if (width > 16) {
867        UYVYToUVRow = UYVYToUVRow_Any_NEON;
868      }
869    }
870    if (IS_ALIGNED(width, 16)) {
871      UYVYToYRow = UYVYToYRow_NEON;
872      UYVYToUVRow = UYVYToUVRow_NEON;
873    }
874  }
875#endif
876
877#if defined(HAS_UYVYTOYROW_SSE2)
878  if (TestCpuFlag(kCpuHasSSE2)) {
879    if (width > 16) {
880      UYVYToUVRow = UYVYToUVRow_Any_SSE2;
881      UYVYToYRow = UYVYToYRow_Any_SSE2;
882    }
883    if (IS_ALIGNED(width, 16)) {
884      UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
885      UYVYToUVRow = UYVYToUVRow_SSE2;
886      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
887        UYVYToYRow = UYVYToYRow_SSE2;
888      }
889    }
890  }
891#elif defined(HAS_UYVYTOYROW_NEON)
892  if (TestCpuFlag(kCpuHasNEON)) {
893    if (width > 8) {
894      UYVYToYRow = UYVYToYRow_Any_NEON;
895      if (width > 16) {
896        UYVYToUVRow = UYVYToUVRow_Any_NEON;
897      }
898    }
899    if (IS_ALIGNED(width, 16)) {
900      UYVYToYRow = UYVYToYRow_NEON;
901      UYVYToUVRow = UYVYToUVRow_NEON;
902    }
903  }
904#endif
905
906  for (int y = 0; y < height - 1; y += 2) {
907    V210ToUYVYRow(src_v210, row, width);
908    V210ToUYVYRow(src_v210 + src_stride_v210, row + kMaxStride, width);
909    UYVYToUVRow(row, kMaxStride, dst_u, dst_v, width);
910    UYVYToYRow(row, dst_y, width);
911    UYVYToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
912    src_v210 += src_stride_v210 * 2;
913    dst_y += dst_stride_y * 2;
914    dst_u += dst_stride_u;
915    dst_v += dst_stride_v;
916  }
917  if (height & 1) {
918    V210ToUYVYRow(src_v210, row, width);
919    UYVYToUVRow(row, 0, dst_u, dst_v, width);
920    UYVYToYRow(row, dst_y, width);
921  }
922  return 0;
923}
924
925LIBYUV_API
926int ARGBToI420(const uint8* src_argb, int src_stride_argb,
927               uint8* dst_y, int dst_stride_y,
928               uint8* dst_u, int dst_stride_u,
929               uint8* dst_v, int dst_stride_v,
930               int width, int height) {
931  if (!src_argb ||
932      !dst_y || !dst_u || !dst_v ||
933      width <= 0 || height == 0) {
934    return -1;
935  }
936  // Negative height means invert the image.
937  if (height < 0) {
938    height = -height;
939    src_argb = src_argb + (height - 1) * src_stride_argb;
940    src_stride_argb = -src_stride_argb;
941  }
942  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix);
943  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
944                      uint8* dst_u, uint8* dst_v, int width);
945
946  ARGBToYRow = ARGBToYRow_C;
947  ARGBToUVRow = ARGBToUVRow_C;
948#if defined(HAS_ARGBTOYROW_SSSE3)
949  if (TestCpuFlag(kCpuHasSSSE3)) {
950    if (width > 16) {
951      ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
952      ARGBToYRow = ARGBToYRow_Any_SSSE3;
953    }
954    if (IS_ALIGNED(width, 16)) {
955      ARGBToUVRow = ARGBToUVRow_Unaligned_SSSE3;
956      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
957      if (IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
958        ARGBToUVRow = ARGBToUVRow_SSSE3;
959        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
960          ARGBToYRow = ARGBToYRow_SSSE3;
961        }
962      }
963    }
964  }
965#endif
966
967  for (int y = 0; y < height - 1; y += 2) {
968    ARGBToUVRow(src_argb, src_stride_argb, dst_u, dst_v, width);
969    ARGBToYRow(src_argb, dst_y, width);
970    ARGBToYRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width);
971    src_argb += src_stride_argb * 2;
972    dst_y += dst_stride_y * 2;
973    dst_u += dst_stride_u;
974    dst_v += dst_stride_v;
975  }
976  if (height & 1) {
977    ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
978    ARGBToYRow(src_argb, dst_y, width);
979  }
980  return 0;
981}
982
983LIBYUV_API
984int BGRAToI420(const uint8* src_bgra, int src_stride_bgra,
985               uint8* dst_y, int dst_stride_y,
986               uint8* dst_u, int dst_stride_u,
987               uint8* dst_v, int dst_stride_v,
988               int width, int height) {
989  if (!src_bgra ||
990      !dst_y || !dst_u || !dst_v ||
991      width <= 0 || height == 0) {
992    return -1;
993  }
994  // Negative height means invert the image.
995  if (height < 0) {
996    height = -height;
997    src_bgra = src_bgra + (height - 1) * src_stride_bgra;
998    src_stride_bgra = -src_stride_bgra;
999  }
1000  void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int pix);
1001  void (*BGRAToUVRow)(const uint8* src_bgra0, int src_stride_bgra,
1002                      uint8* dst_u, uint8* dst_v, int width);
1003
1004  BGRAToYRow = BGRAToYRow_C;
1005  BGRAToUVRow = BGRAToUVRow_C;
1006#if defined(HAS_BGRATOYROW_SSSE3)
1007  if (TestCpuFlag(kCpuHasSSSE3)) {
1008    if (width > 16) {
1009      BGRAToUVRow = BGRAToUVRow_Any_SSSE3;
1010      BGRAToYRow = BGRAToYRow_Any_SSSE3;
1011    }
1012    if (IS_ALIGNED(width, 16)) {
1013      BGRAToUVRow = BGRAToUVRow_Unaligned_SSSE3;
1014      BGRAToYRow = BGRAToYRow_Unaligned_SSSE3;
1015      if (IS_ALIGNED(src_bgra, 16) && IS_ALIGNED(src_stride_bgra, 16)) {
1016        BGRAToUVRow = BGRAToUVRow_SSSE3;
1017        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1018          BGRAToYRow = BGRAToYRow_SSSE3;
1019        }
1020      }
1021    }
1022  }
1023#endif
1024
1025  for (int y = 0; y < height - 1; y += 2) {
1026    BGRAToUVRow(src_bgra, src_stride_bgra, dst_u, dst_v, width);
1027    BGRAToYRow(src_bgra, dst_y, width);
1028    BGRAToYRow(src_bgra + src_stride_bgra, dst_y + dst_stride_y, width);
1029    src_bgra += src_stride_bgra * 2;
1030    dst_y += dst_stride_y * 2;
1031    dst_u += dst_stride_u;
1032    dst_v += dst_stride_v;
1033  }
1034  if (height & 1) {
1035    BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width);
1036    BGRAToYRow(src_bgra, dst_y, width);
1037  }
1038  return 0;
1039}
1040
1041LIBYUV_API
1042int ABGRToI420(const uint8* src_abgr, int src_stride_abgr,
1043               uint8* dst_y, int dst_stride_y,
1044               uint8* dst_u, int dst_stride_u,
1045               uint8* dst_v, int dst_stride_v,
1046               int width, int height) {
1047  if (!src_abgr ||
1048      !dst_y || !dst_u || !dst_v ||
1049      width <= 0 || height == 0) {
1050    return -1;
1051  }
1052  // Negative height means invert the image.
1053  if (height < 0) {
1054    height = -height;
1055    src_abgr = src_abgr + (height - 1) * src_stride_abgr;
1056    src_stride_abgr = -src_stride_abgr;
1057  }
1058  void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int pix);
1059  void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr,
1060                      uint8* dst_u, uint8* dst_v, int width);
1061
1062  ABGRToYRow = ABGRToYRow_C;
1063  ABGRToUVRow = ABGRToUVRow_C;
1064#if defined(HAS_ABGRTOYROW_SSSE3)
1065  if (TestCpuFlag(kCpuHasSSSE3)) {
1066    if (width > 16) {
1067      ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
1068      ABGRToYRow = ABGRToYRow_Any_SSSE3;
1069    }
1070    if (IS_ALIGNED(width, 16)) {
1071      ABGRToUVRow = ABGRToUVRow_Unaligned_SSSE3;
1072      ABGRToYRow = ABGRToYRow_Unaligned_SSSE3;
1073      if (IS_ALIGNED(src_abgr, 16) && IS_ALIGNED(src_stride_abgr, 16)) {
1074        ABGRToUVRow = ABGRToUVRow_SSSE3;
1075        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1076          ABGRToYRow = ABGRToYRow_SSSE3;
1077        }
1078      }
1079    }
1080  }
1081#endif
1082
1083  for (int y = 0; y < height - 1; y += 2) {
1084    ABGRToUVRow(src_abgr, src_stride_abgr, dst_u, dst_v, width);
1085    ABGRToYRow(src_abgr, dst_y, width);
1086    ABGRToYRow(src_abgr + src_stride_abgr, dst_y + dst_stride_y, width);
1087    src_abgr += src_stride_abgr * 2;
1088    dst_y += dst_stride_y * 2;
1089    dst_u += dst_stride_u;
1090    dst_v += dst_stride_v;
1091  }
1092  if (height & 1) {
1093    ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
1094    ABGRToYRow(src_abgr, dst_y, width);
1095  }
1096  return 0;
1097}
1098
1099LIBYUV_API
1100int RGBAToI420(const uint8* src_rgba, int src_stride_rgba,
1101               uint8* dst_y, int dst_stride_y,
1102               uint8* dst_u, int dst_stride_u,
1103               uint8* dst_v, int dst_stride_v,
1104               int width, int height) {
1105  if (!src_rgba ||
1106      !dst_y || !dst_u || !dst_v ||
1107      width <= 0 || height == 0) {
1108    return -1;
1109  }
1110  // Negative height means invert the image.
1111  if (height < 0) {
1112    height = -height;
1113    src_rgba = src_rgba + (height - 1) * src_stride_rgba;
1114    src_stride_rgba = -src_stride_rgba;
1115  }
1116  void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int pix);
1117  void (*RGBAToUVRow)(const uint8* src_rgba0, int src_stride_rgba,
1118                      uint8* dst_u, uint8* dst_v, int width);
1119
1120  RGBAToYRow = RGBAToYRow_C;
1121  RGBAToUVRow = RGBAToUVRow_C;
1122#if defined(HAS_RGBATOYROW_SSSE3)
1123  if (TestCpuFlag(kCpuHasSSSE3)) {
1124    if (width > 16) {
1125      RGBAToUVRow = RGBAToUVRow_Any_SSSE3;
1126      RGBAToYRow = RGBAToYRow_Any_SSSE3;
1127    }
1128    if (IS_ALIGNED(width, 16)) {
1129      RGBAToUVRow = RGBAToUVRow_Unaligned_SSSE3;
1130      RGBAToYRow = RGBAToYRow_Unaligned_SSSE3;
1131      if (IS_ALIGNED(src_rgba, 16) && IS_ALIGNED(src_stride_rgba, 16)) {
1132        RGBAToUVRow = RGBAToUVRow_SSSE3;
1133        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1134          RGBAToYRow = RGBAToYRow_SSSE3;
1135        }
1136      }
1137    }
1138  }
1139#endif
1140
1141  for (int y = 0; y < height - 1; y += 2) {
1142    RGBAToUVRow(src_rgba, src_stride_rgba, dst_u, dst_v, width);
1143    RGBAToYRow(src_rgba, dst_y, width);
1144    RGBAToYRow(src_rgba + src_stride_rgba, dst_y + dst_stride_y, width);
1145    src_rgba += src_stride_rgba * 2;
1146    dst_y += dst_stride_y * 2;
1147    dst_u += dst_stride_u;
1148    dst_v += dst_stride_v;
1149  }
1150  if (height & 1) {
1151    RGBAToUVRow(src_rgba, 0, dst_u, dst_v, width);
1152    RGBAToYRow(src_rgba, dst_y, width);
1153  }
1154  return 0;
1155}
1156
1157LIBYUV_API
1158int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24,
1159                uint8* dst_y, int dst_stride_y,
1160                uint8* dst_u, int dst_stride_u,
1161                uint8* dst_v, int dst_stride_v,
1162                int width, int height) {
1163  if (width * 4 > kMaxStride) {  // Row buffer is required.
1164    return -1;
1165  } else if (!src_rgb24 ||
1166             !dst_y || !dst_u || !dst_v ||
1167             width <= 0 || height == 0) {
1168      return -1;
1169  }
1170  // Negative height means invert the image.
1171  if (height < 0) {
1172    height = -height;
1173    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
1174    src_stride_rgb24 = -src_stride_rgb24;
1175  }
1176  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1177  void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix);
1178
1179  RGB24ToARGBRow = RGB24ToARGBRow_C;
1180#if defined(HAS_RGB24TOARGBROW_SSSE3)
1181  if (TestCpuFlag(kCpuHasSSSE3) &&
1182      TestReadSafe(src_rgb24, src_stride_rgb24, width, height, 3, 48)) {
1183    RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
1184  }
1185#endif
1186
1187  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix);
1188  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1189                      uint8* dst_u, uint8* dst_v, int width);
1190
1191  ARGBToYRow = ARGBToYRow_C;
1192  ARGBToUVRow = ARGBToUVRow_C;
1193#if defined(HAS_ARGBTOYROW_SSSE3)
1194  if (TestCpuFlag(kCpuHasSSSE3)) {
1195    if (width > 16) {
1196      ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1197    }
1198    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1199    if (IS_ALIGNED(width, 16)) {
1200      ARGBToUVRow = ARGBToUVRow_SSSE3;
1201      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1202      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1203        ARGBToYRow = ARGBToYRow_SSSE3;
1204      }
1205    }
1206  }
1207#endif
1208
1209  for (int y = 0; y < height - 1; y += 2) {
1210    RGB24ToARGBRow(src_rgb24, row, width);
1211    RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kMaxStride, width);
1212    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1213    ARGBToYRow(row, dst_y, width);
1214    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1215    src_rgb24 += src_stride_rgb24 * 2;
1216    dst_y += dst_stride_y * 2;
1217    dst_u += dst_stride_u;
1218    dst_v += dst_stride_v;
1219  }
1220  if (height & 1) {
1221    RGB24ToARGBRow_C(src_rgb24, row, width);
1222    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1223    ARGBToYRow(row, dst_y, width);
1224  }
1225  return 0;
1226}
1227
1228LIBYUV_API
1229int RAWToI420(const uint8* src_raw, int src_stride_raw,
1230              uint8* dst_y, int dst_stride_y,
1231              uint8* dst_u, int dst_stride_u,
1232              uint8* dst_v, int dst_stride_v,
1233              int width, int height) {
1234  if (width * 4 > kMaxStride) {  // Row buffer is required.
1235    return -1;
1236  } else if (!src_raw ||
1237             !dst_y || !dst_u || !dst_v ||
1238             width <= 0 || height == 0) {
1239      return -1;
1240  }
1241  // Negative height means invert the image.
1242  if (height < 0) {
1243    height = -height;
1244    src_raw = src_raw + (height - 1) * src_stride_raw;
1245    src_stride_raw = -src_stride_raw;
1246  }
1247  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1248  void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix);
1249
1250  RAWToARGBRow = RAWToARGBRow_C;
1251#if defined(HAS_RAWTOARGBROW_SSSE3)
1252  if (TestCpuFlag(kCpuHasSSSE3) &&
1253      TestReadSafe(src_raw, src_stride_raw, width, height, 3, 48)) {
1254    RAWToARGBRow = RAWToARGBRow_SSSE3;
1255  }
1256#endif
1257
1258  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix);
1259  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1260                      uint8* dst_u, uint8* dst_v, int width);
1261
1262  ARGBToYRow = ARGBToYRow_C;
1263  ARGBToUVRow = ARGBToUVRow_C;
1264#if defined(HAS_ARGBTOYROW_SSSE3)
1265  if (TestCpuFlag(kCpuHasSSSE3)) {
1266    if (width > 16) {
1267      ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1268    }
1269    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1270    if (IS_ALIGNED(width, 16)) {
1271      ARGBToUVRow = ARGBToUVRow_SSSE3;
1272      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1273      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1274        ARGBToYRow = ARGBToYRow_SSSE3;
1275      }
1276    }
1277  }
1278#endif
1279
1280  for (int y = 0; y < height - 1; y += 2) {
1281    RAWToARGBRow(src_raw, row, width);
1282    RAWToARGBRow(src_raw + src_stride_raw, row + kMaxStride, width);
1283    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1284    ARGBToYRow(row, dst_y, width);
1285    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1286    src_raw += src_stride_raw * 2;
1287    dst_y += dst_stride_y * 2;
1288    dst_u += dst_stride_u;
1289    dst_v += dst_stride_v;
1290  }
1291  if (height & 1) {
1292    RAWToARGBRow_C(src_raw, row, width);
1293    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1294    ARGBToYRow(row, dst_y, width);
1295  }
1296  return 0;
1297}
1298
1299LIBYUV_API
1300int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565,
1301                 uint8* dst_y, int dst_stride_y,
1302                 uint8* dst_u, int dst_stride_u,
1303                 uint8* dst_v, int dst_stride_v,
1304                 int width, int height) {
1305  if (width * 4 > kMaxStride) {  // Row buffer is required.
1306    return -1;
1307  } else if (!src_rgb565 ||
1308             !dst_y || !dst_u || !dst_v ||
1309             width <= 0 || height == 0) {
1310    return -1;
1311  }
1312  // Negative height means invert the image.
1313  if (height < 0) {
1314    height = -height;
1315    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
1316    src_stride_rgb565 = -src_stride_rgb565;
1317  }
1318  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1319  void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix);
1320
1321  RGB565ToARGBRow = RGB565ToARGBRow_C;
1322#if defined(HAS_RGB565TOARGBROW_SSE2)
1323  if (TestCpuFlag(kCpuHasSSE2) &&
1324      TestReadSafe(src_rgb565, src_stride_rgb565, width, height, 2, 16)) {
1325    RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
1326  }
1327#endif
1328
1329  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix);
1330  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1331                      uint8* dst_u, uint8* dst_v, int width);
1332
1333  ARGBToYRow = ARGBToYRow_C;
1334  ARGBToUVRow = ARGBToUVRow_C;
1335#if defined(HAS_ARGBTOYROW_SSSE3)
1336  if (TestCpuFlag(kCpuHasSSSE3)) {
1337    if (width > 16) {
1338      ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1339    }
1340    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1341    if (IS_ALIGNED(width, 16)) {
1342      ARGBToUVRow = ARGBToUVRow_SSSE3;
1343      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1344      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1345        ARGBToYRow = ARGBToYRow_SSSE3;
1346      }
1347    }
1348  }
1349#endif
1350
1351  for (int y = 0; y < height - 1; y += 2) {
1352    RGB565ToARGBRow(src_rgb565, row, width);
1353    RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kMaxStride, width);
1354    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1355    ARGBToYRow(row, dst_y, width);
1356    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1357    src_rgb565 += src_stride_rgb565 * 2;
1358    dst_y += dst_stride_y * 2;
1359    dst_u += dst_stride_u;
1360    dst_v += dst_stride_v;
1361  }
1362  if (height & 1) {
1363    RGB565ToARGBRow_C(src_rgb565, row, width);
1364    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1365    ARGBToYRow(row, dst_y, width);
1366  }
1367  return 0;
1368}
1369
1370LIBYUV_API
1371int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555,
1372                 uint8* dst_y, int dst_stride_y,
1373                 uint8* dst_u, int dst_stride_u,
1374                 uint8* dst_v, int dst_stride_v,
1375                 int width, int height) {
1376  if (width * 4 > kMaxStride) {  // Row buffer is required.
1377    return -1;
1378  } else if (!src_argb1555 ||
1379             !dst_y || !dst_u || !dst_v ||
1380             width <= 0 || height == 0) {
1381      return -1;
1382  }
1383  // Negative height means invert the image.
1384  if (height < 0) {
1385    height = -height;
1386    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
1387    src_stride_argb1555 = -src_stride_argb1555;
1388  }
1389  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1390  void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix);
1391
1392  ARGB1555ToARGBRow = ARGB1555ToARGBRow_C;
1393#if defined(HAS_ARGB1555TOARGBROW_SSE2)
1394  if (TestCpuFlag(kCpuHasSSE2) &&
1395      TestReadSafe(src_argb1555, src_stride_argb1555, width, height, 2, 16)) {
1396    ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
1397  }
1398#endif
1399
1400  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix);
1401  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1402                      uint8* dst_u, uint8* dst_v, int width);
1403
1404  ARGBToYRow = ARGBToYRow_C;
1405  ARGBToUVRow = ARGBToUVRow_C;
1406#if defined(HAS_ARGBTOYROW_SSSE3)
1407  if (TestCpuFlag(kCpuHasSSSE3)) {
1408    if (width > 16) {
1409      ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1410    }
1411    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1412    if (IS_ALIGNED(width, 16)) {
1413      ARGBToUVRow = ARGBToUVRow_SSSE3;
1414      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1415      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1416        ARGBToYRow = ARGBToYRow_SSSE3;
1417      }
1418    }
1419  }
1420#endif
1421
1422  for (int y = 0; y < height - 1; y += 2) {
1423    ARGB1555ToARGBRow(src_argb1555, row, width);
1424    ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555,
1425                      row + kMaxStride, width);
1426    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1427    ARGBToYRow(row, dst_y, width);
1428    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1429    src_argb1555 += src_stride_argb1555 * 2;
1430    dst_y += dst_stride_y * 2;
1431    dst_u += dst_stride_u;
1432    dst_v += dst_stride_v;
1433  }
1434  if (height & 1) {
1435    ARGB1555ToARGBRow_C(src_argb1555, row, width);
1436    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1437    ARGBToYRow(row, dst_y, width);
1438  }
1439  return 0;
1440}
1441
1442LIBYUV_API
1443int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444,
1444                   uint8* dst_y, int dst_stride_y,
1445                   uint8* dst_u, int dst_stride_u,
1446                   uint8* dst_v, int dst_stride_v,
1447                   int width, int height) {
1448  if (width * 4 > kMaxStride) {  // Row buffer is required.
1449    return -1;
1450  } else if (!src_argb4444 ||
1451             !dst_y || !dst_u || !dst_v ||
1452             width <= 0 || height == 0) {
1453      return -1;
1454  }
1455  // Negative height means invert the image.
1456  if (height < 0) {
1457    height = -height;
1458    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
1459    src_stride_argb4444 = -src_stride_argb4444;
1460  }
1461  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1462  void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix);
1463
1464  ARGB4444ToARGBRow = ARGB4444ToARGBRow_C;
1465#if defined(HAS_ARGB4444TOARGBROW_SSE2)
1466  if (TestCpuFlag(kCpuHasSSE2) &&
1467      TestReadSafe(src_argb4444, src_stride_argb4444, width, height, 2, 16)) {
1468    ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
1469  }
1470#endif
1471
1472  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix);
1473  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1474                      uint8* dst_u, uint8* dst_v, int width);
1475
1476  ARGBToYRow = ARGBToYRow_C;
1477  ARGBToUVRow = ARGBToUVRow_C;
1478#if defined(HAS_ARGBTOYROW_SSSE3)
1479  if (TestCpuFlag(kCpuHasSSSE3)) {
1480    if (width > 16) {
1481      ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1482    }
1483    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1484    if (IS_ALIGNED(width, 16)) {
1485      ARGBToUVRow = ARGBToUVRow_SSSE3;
1486      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1487      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
1488        ARGBToYRow = ARGBToYRow_SSSE3;
1489      }
1490    }
1491  }
1492#endif
1493
1494  for (int y = 0; y < height - 1; y += 2) {
1495    ARGB4444ToARGBRow(src_argb4444, row, width);
1496    ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444,
1497                      row + kMaxStride, width);
1498    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1499    ARGBToYRow(row, dst_y, width);
1500    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1501    src_argb4444 += src_stride_argb4444 * 2;
1502    dst_y += dst_stride_y * 2;
1503    dst_u += dst_stride_u;
1504    dst_v += dst_stride_v;
1505  }
1506  if (height & 1) {
1507    ARGB4444ToARGBRow_C(src_argb4444, row, width);
1508    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1509    ARGBToYRow(row, dst_y, width);
1510  }
1511  return 0;
1512}
1513
1514#ifdef HAVE_JPEG
1515struct I420Buffers {
1516  uint8* y;
1517  int y_stride;
1518  uint8* u;
1519  int u_stride;
1520  uint8* v;
1521  int v_stride;
1522  int w;
1523  int h;
1524};
1525
1526static void JpegCopyI420(void* opaque,
1527                         const uint8* const* data,
1528                         const int* strides,
1529                         int rows) {
1530  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
1531  I420Copy(data[0], strides[0],
1532           data[1], strides[1],
1533           data[2], strides[2],
1534           dest->y, dest->y_stride,
1535           dest->u, dest->u_stride,
1536           dest->v, dest->v_stride,
1537           dest->w, rows);
1538  dest->y += rows * dest->y_stride;
1539  dest->u += ((rows + 1) >> 1) * dest->u_stride;
1540  dest->v += ((rows + 1) >> 1) * dest->v_stride;
1541  dest->h -= rows;
1542}
1543
1544static void JpegI422ToI420(void* opaque,
1545                           const uint8* const* data,
1546                           const int* strides,
1547                           int rows) {
1548  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
1549  I422ToI420(data[0], strides[0],
1550             data[1], strides[1],
1551             data[2], strides[2],
1552             dest->y, dest->y_stride,
1553             dest->u, dest->u_stride,
1554             dest->v, dest->v_stride,
1555             dest->w, rows);
1556  dest->y += rows * dest->y_stride;
1557  dest->u += ((rows + 1) >> 1) * dest->u_stride;
1558  dest->v += ((rows + 1) >> 1) * dest->v_stride;
1559  dest->h -= rows;
1560}
1561
1562static void JpegI444ToI420(void* opaque,
1563                           const uint8* const* data,
1564                           const int* strides,
1565                           int rows) {
1566  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
1567  I444ToI420(data[0], strides[0],
1568             data[1], strides[1],
1569             data[2], strides[2],
1570             dest->y, dest->y_stride,
1571             dest->u, dest->u_stride,
1572             dest->v, dest->v_stride,
1573             dest->w, rows);
1574  dest->y += rows * dest->y_stride;
1575  dest->u += ((rows + 1) >> 1) * dest->u_stride;
1576  dest->v += ((rows + 1) >> 1) * dest->v_stride;
1577  dest->h -= rows;
1578}
1579
1580static void JpegI411ToI420(void* opaque,
1581                           const uint8* const* data,
1582                           const int* strides,
1583                           int rows) {
1584  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
1585  I411ToI420(data[0], strides[0],
1586             data[1], strides[1],
1587             data[2], strides[2],
1588             dest->y, dest->y_stride,
1589             dest->u, dest->u_stride,
1590             dest->v, dest->v_stride,
1591             dest->w, rows);
1592  dest->y += rows * dest->y_stride;
1593  dest->u += ((rows + 1) >> 1) * dest->u_stride;
1594  dest->v += ((rows + 1) >> 1) * dest->v_stride;
1595  dest->h -= rows;
1596}
1597
1598static void JpegI400ToI420(void* opaque,
1599                           const uint8* const* data,
1600                           const int* strides,
1601                           int rows) {
1602  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
1603  I400ToI420(data[0], strides[0],
1604             dest->y, dest->y_stride,
1605             dest->u, dest->u_stride,
1606             dest->v, dest->v_stride,
1607             dest->w, rows);
1608  dest->y += rows * dest->y_stride;
1609  dest->u += ((rows + 1) >> 1) * dest->u_stride;
1610  dest->v += ((rows + 1) >> 1) * dest->v_stride;
1611  dest->h -= rows;
1612}
1613
1614// MJPG (Motion JPeg) to I420
1615// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
1616LIBYUV_API
1617int MJPGToI420(const uint8* sample,
1618               size_t sample_size,
1619               uint8* y, int y_stride,
1620               uint8* u, int u_stride,
1621               uint8* v, int v_stride,
1622               int w, int h,
1623               int dw, int dh) {
1624  if (sample_size == kUnknownDataSize) {
1625    // ERROR: MJPEG frame size unknown
1626    return -1;
1627  }
1628
1629  // TODO(fbarchard): Port to C
1630  MJpegDecoder mjpeg_decoder;
1631  bool ret = mjpeg_decoder.LoadFrame(sample, sample_size);
1632  if (ret && (mjpeg_decoder.GetWidth() != w ||
1633              mjpeg_decoder.GetHeight() != h)) {
1634    // ERROR: MJPEG frame has unexpected dimensions
1635    mjpeg_decoder.UnloadFrame();
1636    return 1;  // runtime failure
1637  }
1638  if (ret) {
1639    I420Buffers bufs = { y, y_stride, u, u_stride, v, v_stride, dw, dh };
1640    // YUV420
1641    if (mjpeg_decoder.GetColorSpace() ==
1642            MJpegDecoder::kColorSpaceYCbCr &&
1643        mjpeg_decoder.GetNumComponents() == 3 &&
1644        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
1645        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
1646        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
1647        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
1648        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
1649        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
1650      ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh);
1651    // YUV422
1652    } else if (mjpeg_decoder.GetColorSpace() ==
1653                   MJpegDecoder::kColorSpaceYCbCr &&
1654               mjpeg_decoder.GetNumComponents() == 3 &&
1655               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
1656               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
1657               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
1658               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
1659               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
1660               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
1661      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh);
1662    // YUV444
1663    } else if (mjpeg_decoder.GetColorSpace() ==
1664                   MJpegDecoder::kColorSpaceYCbCr &&
1665               mjpeg_decoder.GetNumComponents() == 3 &&
1666               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
1667               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
1668               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
1669               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
1670               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
1671               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
1672      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh);
1673    // YUV411
1674    } else if (mjpeg_decoder.GetColorSpace() ==
1675                   MJpegDecoder::kColorSpaceYCbCr &&
1676               mjpeg_decoder.GetNumComponents() == 3 &&
1677               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
1678               mjpeg_decoder.GetHorizSampFactor(0) == 4 &&
1679               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
1680               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
1681               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
1682               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
1683      ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToI420, &bufs, dw, dh);
1684    // YUV400
1685    } else if (mjpeg_decoder.GetColorSpace() ==
1686                   MJpegDecoder::kColorSpaceGrayscale &&
1687               mjpeg_decoder.GetNumComponents() == 1 &&
1688               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
1689               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
1690      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh);
1691    } else {
1692      // TODO(fbarchard): Implement conversion for any other colorspace/sample
1693      // factors that occur in practice. 411 is supported by libjpeg
1694      // ERROR: Unable to convert MJPEG frame because format is not supported
1695      mjpeg_decoder.UnloadFrame();
1696      return 1;
1697    }
1698  }
1699  return 0;
1700}
1701#endif
1702
1703// Convert camera sample to I420 with cropping, rotation and vertical flip.
1704// src_width is used for source stride computation
1705// src_height is used to compute location of planes, and indicate inversion
1706// sample_size is measured in bytes and is the size of the frame.
1707//   With MJPEG it is the compressed size of the frame.
1708LIBYUV_API
1709int ConvertToI420(const uint8* sample,
1710#ifdef HAVE_JPEG
1711                  size_t sample_size,
1712#else
1713                  size_t /* sample_size */,
1714#endif
1715                  uint8* y, int y_stride,
1716                  uint8* u, int u_stride,
1717                  uint8* v, int v_stride,
1718                  int crop_x, int crop_y,
1719                  int src_width, int src_height,
1720                  int dst_width, int dst_height,
1721                  RotationMode rotation,
1722                  uint32 format) {
1723  if (!y || !u || !v || !sample ||
1724      src_width <= 0 || dst_width <= 0  ||
1725      src_height == 0 || dst_height == 0) {
1726    return -1;
1727  }
1728  int aligned_src_width = (src_width + 1) & ~1;
1729  const uint8* src;
1730  const uint8* src_uv;
1731  int abs_src_height = (src_height < 0) ? -src_height : src_height;
1732  int inv_dst_height = (dst_height < 0) ? -dst_height : dst_height;
1733  if (src_height < 0) {
1734    inv_dst_height = -inv_dst_height;
1735  }
1736  int r = 0;
1737
1738  // One pass rotation is available for some formats. For the rest, convert
1739  // to I420 (with optional vertical flipping) into a temporary I420 buffer,
1740  // and then rotate the I420 to the final destination buffer.
1741  // For in-place conversion, if destination y is same as source sample,
1742  // also enable temporary buffer.
1743  bool need_buf = (rotation && format != FOURCC_I420 &&
1744      format != FOURCC_NV12 && format != FOURCC_NV21 &&
1745      format != FOURCC_YU12 && format != FOURCC_YV12) || y == sample;
1746  uint8* tmp_y = y;
1747  uint8* tmp_u = u;
1748  uint8* tmp_v = v;
1749  int tmp_y_stride = y_stride;
1750  int tmp_u_stride = u_stride;
1751  int tmp_v_stride = v_stride;
1752  uint8* buf = NULL;
1753  int abs_dst_height = (dst_height < 0) ? -dst_height : dst_height;
1754  if (need_buf) {
1755    int y_size = dst_width * abs_dst_height;
1756    int uv_size = ((dst_width + 1) / 2) * ((abs_dst_height + 1) / 2);
1757    buf = new uint8[y_size + uv_size * 2];
1758    if (!buf) {
1759      return 1;  // Out of memory runtime error.
1760    }
1761    y = buf;
1762    u = y + y_size;
1763    v = u + uv_size;
1764    y_stride = dst_width;
1765    u_stride = v_stride = ((dst_width + 1) / 2);
1766  }
1767
1768  switch (format) {
1769    // Single plane formats
1770    case FOURCC_YUY2:
1771      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
1772      r = YUY2ToI420(src, aligned_src_width * 2,
1773                     y, y_stride,
1774                     u, u_stride,
1775                     v, v_stride,
1776                     dst_width, inv_dst_height);
1777      break;
1778    case FOURCC_UYVY:
1779      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
1780      r = UYVYToI420(src, aligned_src_width * 2,
1781                     y, y_stride,
1782                     u, u_stride,
1783                     v, v_stride,
1784                     dst_width, inv_dst_height);
1785      break;
1786    case FOURCC_V210:
1787      // stride is multiple of 48 pixels (128 bytes).
1788      // pixels come in groups of 6 = 16 bytes
1789      src = sample + (aligned_src_width + 47) / 48 * 128 * crop_y +
1790            crop_x / 6 * 16;
1791      r = V210ToI420(src, (aligned_src_width + 47) / 48 * 128,
1792                     y, y_stride,
1793                     u, u_stride,
1794                     v, v_stride,
1795                     dst_width, inv_dst_height);
1796      break;
1797    case FOURCC_24BG:
1798      src = sample + (src_width * crop_y + crop_x) * 3;
1799      r = RGB24ToI420(src, src_width * 3,
1800                      y, y_stride,
1801                      u, u_stride,
1802                      v, v_stride,
1803                      dst_width, inv_dst_height);
1804      break;
1805    case FOURCC_RAW:
1806      src = sample + (src_width * crop_y + crop_x) * 3;
1807      r = RAWToI420(src, src_width * 3,
1808                    y, y_stride,
1809                    u, u_stride,
1810                    v, v_stride,
1811                    dst_width, inv_dst_height);
1812      break;
1813    case FOURCC_ARGB:
1814      src = sample + (src_width * crop_y + crop_x) * 4;
1815      r = ARGBToI420(src, src_width * 4,
1816                     y, y_stride,
1817                     u, u_stride,
1818                     v, v_stride,
1819                     dst_width, inv_dst_height);
1820      break;
1821    case FOURCC_BGRA:
1822      src = sample + (src_width * crop_y + crop_x) * 4;
1823      r = BGRAToI420(src, src_width * 4,
1824                     y, y_stride,
1825                     u, u_stride,
1826                     v, v_stride,
1827                     dst_width, inv_dst_height);
1828      break;
1829    case FOURCC_ABGR:
1830      src = sample + (src_width * crop_y + crop_x) * 4;
1831      r = ABGRToI420(src, src_width * 4,
1832                     y, y_stride,
1833                     u, u_stride,
1834                     v, v_stride,
1835                     dst_width, inv_dst_height);
1836      break;
1837    case FOURCC_RGBA:
1838      src = sample + (src_width * crop_y + crop_x) * 4;
1839      r = RGBAToI420(src, src_width * 4,
1840                     y, y_stride,
1841                     u, u_stride,
1842                     v, v_stride,
1843                     dst_width, inv_dst_height);
1844      break;
1845    case FOURCC_RGBP:
1846      src = sample + (src_width * crop_y + crop_x) * 2;
1847      r = RGB565ToI420(src, src_width * 2,
1848                       y, y_stride,
1849                       u, u_stride,
1850                       v, v_stride,
1851                       dst_width, inv_dst_height);
1852      break;
1853    case FOURCC_RGBO:
1854      src = sample + (src_width * crop_y + crop_x) * 2;
1855      r = ARGB1555ToI420(src, src_width * 2,
1856                         y, y_stride,
1857                         u, u_stride,
1858                         v, v_stride,
1859                         dst_width, inv_dst_height);
1860      break;
1861    case FOURCC_R444:
1862      src = sample + (src_width * crop_y + crop_x) * 2;
1863      r = ARGB4444ToI420(src, src_width * 2,
1864                         y, y_stride,
1865                         u, u_stride,
1866                         v, v_stride,
1867                         dst_width, inv_dst_height);
1868      break;
1869    // TODO(fbarchard): Support cropping Bayer by odd numbers
1870    // by adjusting fourcc.
1871    case FOURCC_BGGR:
1872      src = sample + (src_width * crop_y + crop_x);
1873      r = BayerBGGRToI420(src, src_width,
1874                          y, y_stride,
1875                          u, u_stride,
1876                          v, v_stride,
1877                          dst_width, inv_dst_height);
1878      break;
1879
1880    case FOURCC_GBRG:
1881      src = sample + (src_width * crop_y + crop_x);
1882      r = BayerGBRGToI420(src, src_width,
1883                          y, y_stride,
1884                          u, u_stride,
1885                          v, v_stride,
1886                          dst_width, inv_dst_height);
1887      break;
1888
1889    case FOURCC_GRBG:
1890      src = sample + (src_width * crop_y + crop_x);
1891      r = BayerGRBGToI420(src, src_width,
1892                          y, y_stride,
1893                          u, u_stride,
1894                          v, v_stride,
1895                          dst_width, inv_dst_height);
1896      break;
1897
1898    case FOURCC_RGGB:
1899      src = sample + (src_width * crop_y + crop_x);
1900      r = BayerRGGBToI420(src, src_width,
1901                          y, y_stride,
1902                          u, u_stride,
1903                          v, v_stride,
1904                          dst_width, inv_dst_height);
1905      break;
1906
1907    case FOURCC_I400:
1908      src = sample + src_width * crop_y + crop_x;
1909      r = I400ToI420(src, src_width,
1910                     y, y_stride,
1911                     u, u_stride,
1912                     v, v_stride,
1913                     dst_width, inv_dst_height);
1914      break;
1915
1916    // Biplanar formats
1917    case FOURCC_NV12:
1918      src = sample + (src_width * crop_y + crop_x);
1919      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
1920      r = NV12ToI420Rotate(src, src_width,
1921                           src_uv, aligned_src_width,
1922                           y, y_stride,
1923                           u, u_stride,
1924                           v, v_stride,
1925                           dst_width, inv_dst_height, rotation);
1926      break;
1927    case FOURCC_NV21:
1928      src = sample + (src_width * crop_y + crop_x);
1929      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
1930      // Call NV12 but with u and v parameters swapped.
1931      r = NV12ToI420Rotate(src, src_width,
1932                           src_uv, aligned_src_width,
1933                           y, y_stride,
1934                           v, v_stride,
1935                           u, u_stride,
1936                           dst_width, inv_dst_height, rotation);
1937      break;
1938    case FOURCC_M420:
1939      src = sample + (src_width * crop_y) * 12 / 8 + crop_x;
1940      r = M420ToI420(src, src_width,
1941                     y, y_stride,
1942                     u, u_stride,
1943                     v, v_stride,
1944                     dst_width, inv_dst_height);
1945      break;
1946    case FOURCC_Q420:
1947      src = sample + (src_width + aligned_src_width * 2) * crop_y + crop_x;
1948      src_uv = sample + (src_width + aligned_src_width * 2) * crop_y +
1949               src_width + crop_x * 2;
1950      r = Q420ToI420(src, src_width * 3,
1951                    src_uv, src_width * 3,
1952                    y, y_stride,
1953                    u, u_stride,
1954                    v, v_stride,
1955                    dst_width, inv_dst_height);
1956      break;
1957    // Triplanar formats
1958    case FOURCC_I420:
1959    case FOURCC_YU12:
1960    case FOURCC_YV12: {
1961      const uint8* src_y = sample + (src_width * crop_y + crop_x);
1962      const uint8* src_u;
1963      const uint8* src_v;
1964      int halfwidth = (src_width + 1) / 2;
1965      int halfheight = (abs_src_height + 1) / 2;
1966      if (format == FOURCC_YV12) {
1967        src_v = sample + src_width * abs_src_height +
1968            (halfwidth * crop_y + crop_x) / 2;
1969        src_u = sample + src_width * abs_src_height +
1970            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
1971      } else {
1972        src_u = sample + src_width * abs_src_height +
1973            (halfwidth * crop_y + crop_x) / 2;
1974        src_v = sample + src_width * abs_src_height +
1975            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
1976      }
1977      r = I420Rotate(src_y, src_width,
1978                     src_u, halfwidth,
1979                     src_v, halfwidth,
1980                     y, y_stride,
1981                     u, u_stride,
1982                     v, v_stride,
1983                     dst_width, inv_dst_height, rotation);
1984      break;
1985    }
1986    case FOURCC_I422:
1987    case FOURCC_YV16: {
1988      const uint8* src_y = sample + src_width * crop_y + crop_x;
1989      const uint8* src_u;
1990      const uint8* src_v;
1991      int halfwidth = (src_width + 1) / 2;
1992      if (format == FOURCC_YV16) {
1993        src_v = sample + src_width * abs_src_height +
1994            halfwidth * crop_y + crop_x / 2;
1995        src_u = sample + src_width * abs_src_height +
1996            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
1997      } else {
1998        src_u = sample + src_width * abs_src_height +
1999            halfwidth * crop_y + crop_x / 2;
2000        src_v = sample + src_width * abs_src_height +
2001            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
2002      }
2003      r = I422ToI420(src_y, src_width,
2004                     src_u, halfwidth,
2005                     src_v, halfwidth,
2006                     y, y_stride,
2007                     u, u_stride,
2008                     v, v_stride,
2009                     dst_width, inv_dst_height);
2010      break;
2011    }
2012    case FOURCC_I444:
2013    case FOURCC_YV24: {
2014      const uint8* src_y = sample + src_width * crop_y + crop_x;
2015      const uint8* src_u;
2016      const uint8* src_v;
2017      if (format == FOURCC_YV24) {
2018        src_v = sample + src_width * (abs_src_height + crop_y) + crop_x;
2019        src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
2020      } else {
2021        src_u = sample + src_width * (abs_src_height + crop_y) + crop_x;
2022        src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
2023      }
2024      r = I444ToI420(src_y, src_width,
2025                     src_u, src_width,
2026                     src_v, src_width,
2027                     y, y_stride,
2028                     u, u_stride,
2029                     v, v_stride,
2030                     dst_width, inv_dst_height);
2031      break;
2032    }
2033    case FOURCC_I411: {
2034      int quarterwidth = (src_width + 3) / 4;
2035      const uint8* src_y = sample + src_width * crop_y + crop_x;
2036      const uint8* src_u = sample + src_width * abs_src_height +
2037          quarterwidth * crop_y + crop_x / 4;
2038      const uint8* src_v = sample + src_width * abs_src_height +
2039          quarterwidth * (abs_src_height + crop_y) + crop_x / 4;
2040      r = I411ToI420(src_y, src_width,
2041                     src_u, quarterwidth,
2042                     src_v, quarterwidth,
2043                     y, y_stride,
2044                     u, u_stride,
2045                     v, v_stride,
2046                     dst_width, inv_dst_height);
2047      break;
2048    }
2049#ifdef HAVE_JPEG
2050    case FOURCC_MJPG:
2051      r = MJPGToI420(sample, sample_size,
2052                     y, y_stride,
2053                     u, u_stride,
2054                     v, v_stride,
2055                     src_width, abs_src_height, dst_width, inv_dst_height);
2056      break;
2057#endif
2058    default:
2059      r = -1;  // unknown fourcc - return failure code.
2060  }
2061
2062  if (need_buf) {
2063    if (!r) {
2064      r = I420Rotate(y, y_stride,
2065                     u, u_stride,
2066                     v, v_stride,
2067                     tmp_y, tmp_y_stride,
2068                     tmp_u, tmp_u_stride,
2069                     tmp_v, tmp_v_stride,
2070                     dst_width, abs_dst_height, rotation);
2071    }
2072    delete buf;
2073  }
2074
2075  return r;
2076}
2077
2078#ifdef __cplusplus
2079}  // extern "C"
2080}  // namespace libyuv
2081#endif
2082