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
25 namespace libyuv {
26 extern "C" {
27 #endif
28 
29 // Copy I420 with optional flipping
30 LIBYUV_API
I420Copy(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)31 int 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))
HalfRow_SSE2(const uint8 * src_uv,int src_uv_stride,uint8 * dst_uv,int pix)69 static 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
HalfRow_SSE2(const uint8 * src_uv,int src_uv_stride,uint8 * dst_uv,int pix)94 static 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 
HalfRow_C(const uint8 * src_uv,int src_uv_stride,uint8 * dst_uv,int pix)118 static 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 
125 LIBYUV_API
I422ToI420(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)126 int 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
194 void 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 
199 void ScaleRowDown2Int_SSE2(const uint8* src_ptr, ptrdiff_t src_stride,
200                            uint8* dst_ptr, int dst_width);
201 #endif
202 void ScaleRowDown2Int_C(const uint8* src_ptr, ptrdiff_t src_stride,
203                         uint8* dst_ptr, int dst_width);
204 
205 LIBYUV_API
I444ToI420(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)206 int 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
276 void 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
283 LIBYUV_API
I411ToI420(const uint8 * src_y,int src_stride_y,const uint8 * src_u,int src_stride_u,const uint8 * src_v,int src_stride_v,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)284 int 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
333 LIBYUV_API
I400ToI420(const uint8 * src_y,int src_stride_y,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)334 int 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 
CopyPlane2(const uint8 * src,int src_stride_0,int src_stride_1,uint8 * dst,int dst_stride_frame,int width,int height)357 static 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.
X420ToI420(const uint8 * src_y,int src_stride_y0,int src_stride_y1,const uint8 * src_uv,int src_stride_uv,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)400 static 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.
458 LIBYUV_API
NV12ToI420(const uint8 * src_y,int src_stride_y,const uint8 * src_uv,int src_stride_uv,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)459 int 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.
474 LIBYUV_API
M420ToI420(const uint8 * src_m420,int src_stride_m420,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)475 int 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
490 LIBYUV_API
Q420ToI420(const uint8 * src_y,int src_stride_y,const uint8 * src_yuy2,int src_stride_yuy2,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)491 int 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 
TestReadSafe(const uint8 * src_yuy2,int src_stride_yuy2,int width,int height,int bpp,int overread)612 static 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.
642 LIBYUV_API
YUY2ToI420(const uint8 * src_yuy2,int src_stride_yuy2,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)643 int 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.
709 LIBYUV_API
UYVYToI420(const uint8 * src_uyvy,int src_stride_uyvy,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)710 int 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
READWORD(const uint8 * p)786 static 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
V210ToUYVYRow_C(const uint8 * src_v210,uint8 * dst_uyvy,int width)796 static 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.
826 LIBYUV_API
V210ToI420(const uint8 * src_v210,int src_stride_v210,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)827 int 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 
925 LIBYUV_API
ARGBToI420(const uint8 * src_argb,int src_stride_argb,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)926 int 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 
983 LIBYUV_API
BGRAToI420(const uint8 * src_bgra,int src_stride_bgra,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)984 int 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 
1041 LIBYUV_API
ABGRToI420(const uint8 * src_abgr,int src_stride_abgr,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1042 int 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 
1099 LIBYUV_API
RGBAToI420(const uint8 * src_rgba,int src_stride_rgba,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1100 int 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 
1157 LIBYUV_API
RGB24ToI420(const uint8 * src_rgb24,int src_stride_rgb24,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1158 int 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 
1228 LIBYUV_API
RAWToI420(const uint8 * src_raw,int src_stride_raw,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1229 int 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 
1299 LIBYUV_API
RGB565ToI420(const uint8 * src_rgb565,int src_stride_rgb565,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1300 int 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 
1370 LIBYUV_API
ARGB1555ToI420(const uint8 * src_argb1555,int src_stride_argb1555,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1371 int 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 
1442 LIBYUV_API
ARGB4444ToI420(const uint8 * src_argb4444,int src_stride_argb4444,uint8 * dst_y,int dst_stride_y,uint8 * dst_u,int dst_stride_u,uint8 * dst_v,int dst_stride_v,int width,int height)1443 int 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
1515 struct 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 
JpegCopyI420(void * opaque,const uint8 * const * data,const int * strides,int rows)1526 static 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 
JpegI422ToI420(void * opaque,const uint8 * const * data,const int * strides,int rows)1544 static 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 
JpegI444ToI420(void * opaque,const uint8 * const * data,const int * strides,int rows)1562 static 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 
JpegI411ToI420(void * opaque,const uint8 * const * data,const int * strides,int rows)1580 static 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 
JpegI400ToI420(void * opaque,const uint8 * const * data,const int * strides,int rows)1598 static 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.
1616 LIBYUV_API
MJPGToI420(const uint8 * sample,size_t sample_size,uint8 * y,int y_stride,uint8 * u,int u_stride,uint8 * v,int v_stride,int w,int h,int dw,int dh)1617 int 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.
1708 LIBYUV_API
ConvertToI420(const uint8 * sample,size_t sample_size,uint8 * y,int y_stride,uint8 * u,int u_stride,uint8 * v,int v_stride,int crop_x,int crop_y,int src_width,int src_height,int dst_width,int dst_height,RotationMode rotation,uint32 format)1709 int 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