convert.cc 71.1 KB
Newer Older
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
1
/*
2
 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
3 4 5 6
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
7
 *  in the file PATENTS. All contributing project authors may
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
8 9 10
 *  be found in the AUTHORS file in the root of the source tree.
 */

11
#include "libyuv/convert.h"
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
12

13 14
#include "libyuv/basic_types.h"
#include "libyuv/cpu_id.h"
15
#include "libyuv/format_conversion.h"
fbarchard@google.com's avatar
fbarchard@google.com committed
16 17 18
#ifdef HAVE_JPEG
#include "libyuv/mjpeg_decoder.h"
#endif
19 20
#include "libyuv/planar_functions.h"
#include "libyuv/rotate.h"
21
#include "libyuv/scale.h"  // For ScalePlane()
22
#include "libyuv/video_common.h"
23
#include "libyuv/row.h"
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
24

25
#ifdef __cplusplus
26
namespace libyuv {
27 28
extern "C" {
#endif
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
29

30
// Copy I420 with optional flipping
31
LIBYUV_API
32 33 34 35 36 37 38 39 40 41 42
int 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) {
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
43
  }
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    int halfheight = (height + 1) >> 1;
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (halfheight - 1) * src_stride_u;
    src_v = src_v + (halfheight - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }

  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;
  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }
  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
  return 0;
64
}
65

66
LIBYUV_API
67
int I422ToI420(const uint8* src_y, int src_stride_y,
68 69
               const uint8* src_u, int src_stride_u,
               const uint8* src_v, int src_stride_v,
70 71 72
               uint8* dst_y, int dst_stride_y,
               uint8* dst_u, int dst_stride_u,
               uint8* dst_v, int dst_stride_v,
73
               int width, int height) {
74 75 76 77 78
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
79 80 81
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
82 83 84 85 86 87 88 89 90
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }
  int halfwidth = (width + 1) >> 1;
  void (*HalfRow)(const uint8* src_uv, int src_uv_stride,
fbarchard@google.com's avatar
fbarchard@google.com committed
91
                  uint8* dst_uv, int pix) = HalfRow_C;
92
#if defined(HAS_HALFROW_SSE2)
93
  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(halfwidth, 16) &&
94 95 96 97 98 99
      IS_ALIGNED(src_u, 16) && IS_ALIGNED(src_stride_u, 16) &&
      IS_ALIGNED(src_v, 16) && IS_ALIGNED(src_stride_v, 16) &&
      IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
      IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
    HalfRow = HalfRow_SSE2;
  }
100 101 102 103 104 105 106 107 108
#endif
#if defined(HAS_HALFROW_AVX2)
  bool clear = false;
  if (TestCpuFlag(kCpuHasAVX2) && IS_ALIGNED(halfwidth, 32)) {
    clear = true;
    HalfRow = HalfRow_AVX2;
  }
#endif
#if defined(HAS_HALFROW_NEON)
109 110 111
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(halfwidth, 16)) {
    HalfRow = HalfRow_NEON;
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
112
#endif
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  // Copy Y plane
  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }

  // SubSample U plane.
  int y;
  for (y = 0; y < height - 1; y += 2) {
    HalfRow(src_u, src_stride_u, dst_u, halfwidth);
    src_u += src_stride_u * 2;
    dst_u += dst_stride_u;
  }
  if (height & 1) {
    HalfRow(src_u, 0, dst_u, halfwidth);
  }

  // SubSample V plane.
  for (y = 0; y < height - 1; y += 2) {
    HalfRow(src_v, src_stride_v, dst_v, halfwidth);
    src_v += src_stride_v * 2;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
    HalfRow(src_v, 0, dst_v, halfwidth);
138
  }
139 140 141 142 143
#if defined(HAS_HALFROW_AVX2)
  if (clear) {
    __asm vzeroupper;
  }
#endif
144 145
  return 0;
}
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
146

147 148
// Blends 32x2 pixels to 16x1
// source in scale.cc
149 150
#if !defined(LIBYUV_DISABLE_NEON) && \
    (defined(__ARM_NEON__) || defined(LIBYUV_NEON))
151
#define HAS_SCALEROWDOWN2_NEON
152
void ScaleRowDown2Int_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
153
                           uint8* dst, int dst_width);
154
#elif !defined(LIBYUV_DISABLE_X86) && \
155 156
    (defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))

157
void ScaleRowDown2Int_SSE2(const uint8* src_ptr, ptrdiff_t src_stride,
158 159
                           uint8* dst_ptr, int dst_width);
#endif
160
void ScaleRowDown2Int_C(const uint8* src_ptr, ptrdiff_t src_stride,
161 162
                        uint8* dst_ptr, int dst_width);

163
LIBYUV_API
164
int I444ToI420(const uint8* src_y, int src_stride_y,
165 166
               const uint8* src_u, int src_stride_u,
               const uint8* src_v, int src_stride_v,
167 168 169
               uint8* dst_y, int dst_stride_y,
               uint8* dst_u, int dst_stride_u,
               uint8* dst_v, int dst_stride_v,
170
               int width, int height) {
171 172 173 174 175
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
176 177 178
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
179 180 181 182 183 184 185 186
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
  }
  int halfwidth = (width + 1) >> 1;
187
  void (*ScaleRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride,
fbarchard@google.com's avatar
fbarchard@google.com committed
188
                        uint8* dst_ptr, int dst_width) = ScaleRowDown2Int_C;
189 190 191 192
#if defined(HAS_SCALEROWDOWN2_NEON)
  if (TestCpuFlag(kCpuHasNEON) &&
      IS_ALIGNED(halfwidth, 16)) {
    ScaleRowDown2 = ScaleRowDown2Int_NEON;
fbarchard@google.com's avatar
fbarchard@google.com committed
193 194
  }
#elif defined(HAS_SCALEROWDOWN2_SSE2)
195
  if (TestCpuFlag(kCpuHasSSE2) &&
196 197 198 199 200 201 202
      IS_ALIGNED(halfwidth, 16) &&
      IS_ALIGNED(src_u, 16) && IS_ALIGNED(src_stride_u, 16) &&
      IS_ALIGNED(src_v, 16) && IS_ALIGNED(src_stride_v, 16) &&
      IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
      IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
    ScaleRowDown2 = ScaleRowDown2Int_SSE2;
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
203
#endif
204

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
  // Copy Y plane
  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }

  // SubSample U plane.
  int y;
  for (y = 0; y < height - 1; y += 2) {
    ScaleRowDown2(src_u, src_stride_u, dst_u, halfwidth);
    src_u += src_stride_u * 2;
    dst_u += dst_stride_u;
  }
  if (height & 1) {
    ScaleRowDown2(src_u, 0, dst_u, halfwidth);
  }

  // SubSample V plane.
  for (y = 0; y < height - 1; y += 2) {
    ScaleRowDown2(src_v, src_stride_v, dst_v, halfwidth);
    src_v += src_stride_v * 2;
    dst_v += dst_stride_v;
226 227
  }
  if (height & 1) {
228
    ScaleRowDown2(src_v, 0, dst_v, halfwidth);
229
  }
230
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
231 232
}

233
// TODO(fbarchard): Enable bilinear when fast enough or specialized upsampler.
234 235
// 411 chroma is 1/4 width, 1x height
// 420 chroma is 1/2 width, 1/2 height
236
LIBYUV_API
237 238 239 240 241 242 243
int 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) {
244 245 246 247 248
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
249 250 251
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
252 253 254 255 256 257
    src_y = src_y + (height - 1) * src_stride_y;
    src_u = src_u + (height - 1) * src_stride_u;
    src_v = src_v + (height - 1) * src_stride_v;
    src_stride_y = -src_stride_y;
    src_stride_u = -src_stride_u;
    src_stride_v = -src_stride_v;
258 259 260 261 262 263 264 265 266 267 268
  }

  // Copy Y plane
  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }

  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;
  int quarterwidth = (width + 3) >> 2;

269 270 271 272
  // Resample U plane from 1/4 width, 1x height to 1/2 width, 1/2 height.
  ScalePlane(src_u, src_stride_u, quarterwidth, height,
             dst_u, dst_stride_u, halfwidth, halfheight,
             kFilterNone);
273 274

  // Resample V plane.
275 276 277
  ScalePlane(src_v, src_stride_v, quarterwidth, height,
             dst_v, dst_stride_v, halfwidth, halfheight,
             kFilterNone);
278 279 280
  return 0;
}

281
// I400 is greyscale typically used in MJPG
282
LIBYUV_API
283 284 285 286
int 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,
287
               int width, int height) {
288 289 290 291
  if (!src_y || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
292 293 294
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
295 296 297 298 299 300 301 302
    src_y = src_y + (height - 1) * src_stride_y;
    src_stride_y = -src_stride_y;
  }
  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;
  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  SetPlane(dst_u, dst_stride_u, halfwidth, halfheight, 128);
  SetPlane(dst_v, dst_stride_v, halfwidth, halfheight, 128);
303 304 305
  return 0;
}

306
static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1,
307
                       uint8* dst, int dst_stride,
308
                       int width, int height) {
309
  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
310 311
#if defined(HAS_COPYROW_X86)
  if (TestCpuFlag(kCpuHasX86) && IS_ALIGNED(width, 4)) {
312
    CopyRow = CopyRow_X86;
313 314
  }
#endif
315
#if defined(HAS_COPYROW_SSE2)
316 317 318 319 320 321
  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) &&
      IS_ALIGNED(src, 16) &&
      IS_ALIGNED(src_stride_0, 16) && IS_ALIGNED(src_stride_1, 16) &&
      IS_ALIGNED(dst, 16) && IS_ALIGNED(dst_stride, 16)) {
    CopyRow = CopyRow_SSE2;
  }
322
#endif
323 324 325 326 327 328 329 330 331
#if defined(HAS_COPYROW_AVX2)
  // TODO(fbarchard): Detect Fast String support.
  if (TestCpuFlag(kCpuHasAVX2)) {
    CopyRow = CopyRow_AVX2;
  }
#endif
#if defined(HAS_COPYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
    CopyRow = CopyRow_NEON;
332 333
  }
#endif
334 335 336 337 338
#if defined(HAS_COPYROW_MIPS)
  if (TestCpuFlag(kCpuHasMIPS)) {
    CopyRow = CopyRow_MIPS;
  }
#endif
339

340
  // Copy plane
341 342
  for (int y = 0; y < height - 1; y += 2) {
    CopyRow(src, dst, width);
343
    CopyRow(src + src_stride_0, dst + dst_stride, width);
344
    src += src_stride_0 + src_stride_1;
345
    dst += dst_stride * 2;
346 347 348
  }
  if (height & 1) {
    CopyRow(src, dst, width);
349
  }
350 351 352 353 354 355
}

// Support converting from FOURCC_M420
// Useful for bandwidth constrained transports like USB 1.0 and 2.0 and for
// easy conversion to I420.
// M420 format description:
356
// M420 is row biplanar 420: 2 rows of Y and 1 row of UV.
357
// Chroma is half width / half height. (420)
358
// src_stride_m420 is row planar. Normally this will be the width in pixels.
359 360 361 362 363 364 365 366 367
//   The UV plane is half width, but 2 values, so src_stride_m420 applies to
//   this as well as the two Y planes.
static int 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) {
368 369 370 371 372
  if (!src_y || !src_uv ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
373 374 375
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
376 377 378 379 380 381 382 383
    int halfheight = (height + 1) >> 1;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_u = dst_u + (halfheight - 1) * dst_stride_u;
    dst_v = dst_v + (halfheight - 1) * dst_stride_v;
    dst_stride_y = -dst_stride_y;
    dst_stride_u = -dst_stride_u;
    dst_stride_v = -dst_stride_v;
  }
384
  // Coalesce contiguous rows.
385
  int halfwidth = (width + 1) >> 1;
386 387 388 389 390 391 392 393 394 395 396 397 398
  int halfheight = (height + 1) >> 1;
  if (src_stride_y0 == width &&
      src_stride_y1 == width &&
      dst_stride_y == width) {
    width = width * height;
    height = 1;
  }
  if (src_stride_uv == width &&
      dst_stride_u * 2 == width &&
      dst_stride_v * 2 == width) {
    halfwidth = halfwidth * halfheight;
    halfheight = 1;
  }
399 400 401
  void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v, int pix) =
      SplitUVRow_C;
#if defined(HAS_SPLITUVROW_SSE2)
402
  if (TestCpuFlag(kCpuHasSSE2) && halfwidth >= 16) {
403
    SplitUVRow = SplitUVRow_Any_SSE2;
404
    if (IS_ALIGNED(halfwidth, 16)) {
405
      SplitUVRow = SplitUVRow_Unaligned_SSE2;
406 407 408
      if (IS_ALIGNED(src_uv, 16) && IS_ALIGNED(src_stride_uv, 16) &&
          IS_ALIGNED(dst_u, 16) && IS_ALIGNED(dst_stride_u, 16) &&
          IS_ALIGNED(dst_v, 16) && IS_ALIGNED(dst_stride_v, 16)) {
409
        SplitUVRow = SplitUVRow_SSE2;
410 411
      }
    }
fbarchard@google.com's avatar
fbarchard@google.com committed
412
  }
413
#endif
414
#if defined(HAS_SPLITUVROW_AVX2)
415
  if (TestCpuFlag(kCpuHasAVX2) && halfwidth >= 32) {
416
    SplitUVRow = SplitUVRow_Any_AVX2;
417
    if (IS_ALIGNED(halfwidth, 32)) {
418
      SplitUVRow = SplitUVRow_AVX2;
419
    }
420
  }
421
#endif
422
#if defined(HAS_SPLITUVROW_NEON)
423
  if (TestCpuFlag(kCpuHasNEON) && halfwidth >= 16) {
424
    SplitUVRow = SplitUVRow_Any_NEON;
425
    if (IS_ALIGNED(halfwidth, 16)) {
426
      SplitUVRow = SplitUVRow_NEON;
427 428 429
    }
  }
#endif
430
#if defined(HAS_SPLITUVROW_MIPS_DSPR2)
431
  if (TestCpuFlag(kCpuHasMIPS_DSPR2) && halfwidth >= 16) {
432
    SplitUVRow = SplitUVRow_Any_MIPS_DSPR2;
433
    if (IS_ALIGNED(halfwidth, 16)) {
434
      SplitUVRow = SplitUVRow_Unaligned_MIPS_DSPR2;
435 436 437
      if (IS_ALIGNED(src_uv, 4) && IS_ALIGNED(src_stride_uv, 4) &&
          IS_ALIGNED(dst_u, 4) && IS_ALIGNED(dst_stride_u, 4) &&
          IS_ALIGNED(dst_v, 4) && IS_ALIGNED(dst_stride_v, 4)) {
438
        SplitUVRow = SplitUVRow_MIPS_DSPR2;
439 440
      }
    }
441
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
442
#endif
443

444
  if (dst_y) {
445 446 447 448 449 450
    if (src_stride_y0 == src_stride_y1) {
      CopyPlane(src_y, src_stride_y0, dst_y, dst_stride_y, width, height);
    } else {
      CopyPlane2(src_y, src_stride_y0, src_stride_y1, dst_y, dst_stride_y,
                 width, height);
    }
451
  }
452 453 454

  for (int y = 0; y < halfheight; ++y) {
    // Copy a row of UV.
455
    SplitUVRow(src_uv, dst_u, dst_v, halfwidth);
456 457 458
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
    src_uv += src_stride_uv;
459 460 461 462
  }
  return 0;
}

463
// Convert NV12 to I420.
464
LIBYUV_API
465 466 467 468 469
int 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,
470
               int width, int height) {
471 472 473 474 475 476 477 478
  return X420ToI420(src_y, src_stride_y, src_stride_y,
                    src_uv, src_stride_uv,
                    dst_y, dst_stride_y,
                    dst_u, dst_stride_u,
                    dst_v, dst_stride_v,
                    width, height);
}

479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
// Convert NV21 to I420.  Same as NV12 but u and v pointers swapped.
LIBYUV_API
int NV21ToI420(const uint8* src_y, int src_stride_y,
               const uint8* src_vu, int src_stride_vu,
               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) {
  return X420ToI420(src_y, src_stride_y, src_stride_y,
                    src_vu, src_stride_vu,
                    dst_y, dst_stride_y,
                    dst_v, dst_stride_v,
                    dst_u, dst_stride_u,
                    width, height);
}

495
// Convert M420 to I420.
496
LIBYUV_API
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
int 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) {
  return X420ToI420(src_m420, src_stride_m420, src_stride_m420 * 2,
                    src_m420 + src_stride_m420 * 2, src_stride_m420 * 3,
                    dst_y, dst_stride_y,
                    dst_u, dst_stride_u,
                    dst_v, dst_stride_v,
                    width, height);
}

// Convert Q420 to I420.
// Format is rows of YY/YUYV
512
LIBYUV_API
513 514 515 516 517 518
int 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) {
519 520 521 522 523
  if (!src_y || !src_yuy2 ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
524 525 526
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
527 528 529 530 531 532 533 534
    int halfheight = (height + 1) >> 1;
    dst_y = dst_y + (height - 1) * dst_stride_y;
    dst_u = dst_u + (halfheight - 1) * dst_stride_u;
    dst_v = dst_v + (halfheight - 1) * dst_stride_v;
    dst_stride_y = -dst_stride_y;
    dst_stride_u = -dst_stride_u;
    dst_stride_v = -dst_stride_v;
  }
535
  // CopyRow for rows of just Y in Q420 copied to Y plane of I420.
536 537
  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
#if defined(HAS_COPYROW_NEON)
538
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
539 540
    CopyRow = CopyRow_NEON;
  }
541 542
#endif
#if defined(HAS_COPYROW_X86)
543 544
  if (IS_ALIGNED(width, 4)) {
    CopyRow = CopyRow_X86;
545
  }
546
#endif
547 548 549 550 551
#if defined(HAS_COPYROW_SSE2)
  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32) &&
      IS_ALIGNED(src_y, 16) && IS_ALIGNED(src_stride_y, 16) &&
      IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
    CopyRow = CopyRow_SSE2;
552 553
  }
#endif
554 555 556 557 558
#if defined(HAS_COPYROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    CopyRow = CopyRow_AVX2;
  }
#endif
559 560 561 562 563
#if defined(HAS_COPYROW_MIPS)
  if (TestCpuFlag(kCpuHasMIPS)) {
    CopyRow = CopyRow_MIPS;
  }
#endif
564

565 566 567 568 569
  void (*YUY2ToUV422Row)(const uint8* src_yuy2, uint8* dst_u, uint8* dst_v,
      int pix) = YUY2ToUV422Row_C;
  void (*YUY2ToYRow)(const uint8* src_yuy2, uint8* dst_y, int pix) =
      YUY2ToYRow_C;
#if defined(HAS_YUY2TOYROW_SSE2)
570 571 572
  if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
573 574 575 576 577
    if (IS_ALIGNED(width, 16)) {
      YUY2ToUV422Row = YUY2ToUV422Row_Unaligned_SSE2;
      YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
      if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
        YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
578 579 580
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          YUY2ToYRow = YUY2ToYRow_SSE2;
        }
581 582 583
      }
    }
  }
584 585 586 587 588 589 590 591 592 593 594 595 596 597
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
  bool clear = false;
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    clear = true;
    YUY2ToUV422Row = YUY2ToUV422Row_Any_AVX2;
    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      YUY2ToUV422Row = YUY2ToUV422Row_AVX2;
      YUY2ToYRow = YUY2ToYRow_AVX2;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_NEON)
598 599 600 601
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
    if (width >= 16) {
      YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
602
    }
603
    if (IS_ALIGNED(width, 16)) {
604
      YUY2ToYRow = YUY2ToYRow_NEON;
605
      YUY2ToUV422Row = YUY2ToUV422Row_NEON;
606
    }
607
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
608 609
#endif

610
  for (int y = 0; y < height - 1; y += 2) {
611
    CopyRow(src_y, dst_y, width);
612
    src_y += src_stride_y;
613
    dst_y += dst_stride_y;
614

615 616 617
    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
    src_yuy2 += src_stride_yuy2;
618 619 620
    dst_y += dst_stride_y;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
621 622 623 624
  }
  if (height & 1) {
    CopyRow(src_y, dst_y, width);
    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
625
  }
626 627 628 629 630
#if defined(HAS_YUY2TOYROW_AVX2)
  if (clear) {
    __asm vzeroupper;
  }
#endif
631
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
632 633
}

634
// Convert YUY2 to I420.
635
LIBYUV_API
636
int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
637 638 639 640
               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) {
641
  // Negative height means invert the image.
642 643
  if (height < 0) {
    height = -height;
644 645 646 647
    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
    src_stride_yuy2 = -src_stride_yuy2;
  }
  void (*YUY2ToUVRow)(const uint8* src_yuy2, int src_stride_yuy2,
648
                      uint8* dst_u, uint8* dst_v, int pix);
649
  void (*YUY2ToYRow)(const uint8* src_yuy2,
650
                     uint8* dst_y, int pix);
651 652 653
  YUY2ToYRow = YUY2ToYRow_C;
  YUY2ToUVRow = YUY2ToUVRow_C;
#if defined(HAS_YUY2TOYROW_SSE2)
654 655 656
  if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    YUY2ToUVRow = YUY2ToUVRow_Any_SSE2;
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
657
    if (IS_ALIGNED(width, 16)) {
658 659 660 661 662 663 664 665
      YUY2ToUVRow = YUY2ToUVRow_Unaligned_SSE2;
      YUY2ToYRow = YUY2ToYRow_Unaligned_SSE2;
      if (IS_ALIGNED(src_yuy2, 16) && IS_ALIGNED(src_stride_yuy2, 16)) {
        YUY2ToUVRow = YUY2ToUVRow_SSE2;
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          YUY2ToYRow = YUY2ToYRow_SSE2;
        }
      }
666
    }
667
  }
668 669 670 671 672 673 674 675 676 677 678 679 680 681
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
  bool clear = false;
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    bool clear = true;
    YUY2ToUVRow = YUY2ToUVRow_Any_AVX2;
    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      YUY2ToUVRow = YUY2ToUVRow_AVX2;
      YUY2ToYRow = YUY2ToYRow_AVX2;
    }
  }
#endif
#if defined(HAS_YUY2TOYROW_NEON)
682 683 684 685
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
    if (width >= 16) {
      YUY2ToUVRow = YUY2ToUVRow_Any_NEON;
686
    }
687
    if (IS_ALIGNED(width, 16)) {
688
      YUY2ToYRow = YUY2ToYRow_NEON;
689
      YUY2ToUVRow = YUY2ToUVRow_NEON;
690 691
    }
  }
692
#endif
693

694 695 696 697 698 699 700 701
  for (int y = 0; y < height - 1; y += 2) {
    YUY2ToUVRow(src_yuy2, src_stride_yuy2, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
    YUY2ToYRow(src_yuy2 + src_stride_yuy2, dst_y + dst_stride_y, width);
    src_yuy2 += src_stride_yuy2 * 2;
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
702
  }
703
  if (height & 1) {
704 705
    YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
706
  }
707 708 709 710 711 712

#if defined(HAS_YUY2TOYROW_AVX2)
  if (clear) {
    __asm vzeroupper;
  }
#endif
713 714 715 716
  return 0;
}

// Convert UYVY to I420.
717
LIBYUV_API
718 719 720 721 722 723 724 725 726 727 728 729
int 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) {
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
    src_stride_uyvy = -src_stride_uyvy;
  }
  void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy,
730
                      uint8* dst_u, uint8* dst_v, int pix);
731
  void (*UYVYToYRow)(const uint8* src_uyvy,
732
                     uint8* dst_y, int pix);
733 734 735
  UYVYToYRow = UYVYToYRow_C;
  UYVYToUVRow = UYVYToUVRow_C;
#if defined(HAS_UYVYTOYROW_SSE2)
736 737 738
  if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    UYVYToUVRow = UYVYToUVRow_Any_SSE2;
    UYVYToYRow = UYVYToYRow_Any_SSE2;
739
    if (IS_ALIGNED(width, 16)) {
740 741 742 743 744 745 746 747
      UYVYToUVRow = UYVYToUVRow_Unaligned_SSE2;
      UYVYToYRow = UYVYToYRow_Unaligned_SSE2;
      if (IS_ALIGNED(src_uyvy, 16) && IS_ALIGNED(src_stride_uyvy, 16)) {
        UYVYToUVRow = UYVYToUVRow_SSE2;
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          UYVYToYRow = UYVYToYRow_SSE2;
        }
      }
748
    }
749
  }
750 751 752 753 754 755 756 757 758 759 760 761 762 763
#endif
#if defined(HAS_UYVYTOYROW_AVX2)
  bool clear = false;
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    bool clear = true;
    UYVYToUVRow = UYVYToUVRow_Any_AVX2;
    UYVYToYRow = UYVYToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
      UYVYToUVRow = UYVYToUVRow_AVX2;
      UYVYToYRow = UYVYToYRow_AVX2;
    }
  }
#endif
#if defined(HAS_UYVYTOYROW_NEON)
764 765 766 767
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    UYVYToYRow = UYVYToYRow_Any_NEON;
    if (width >= 16) {
      UYVYToUVRow = UYVYToUVRow_Any_NEON;
768
    }
769
    if (IS_ALIGNED(width, 16)) {
770
      UYVYToYRow = UYVYToYRow_NEON;
771
      UYVYToUVRow = UYVYToUVRow_NEON;
772 773
    }
  }
774
#endif
775

776 777 778 779 780
  for (int y = 0; y < height - 1; y += 2) {
    UYVYToUVRow(src_uyvy, src_stride_uyvy, dst_u, dst_v, width);
    UYVYToYRow(src_uyvy, dst_y, width);
    UYVYToYRow(src_uyvy + src_stride_uyvy, dst_y + dst_stride_y, width);
    src_uyvy += src_stride_uyvy * 2;
781 782 783
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
784
  }
785
  if (height & 1) {
786 787 788
    UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width);
    UYVYToYRow(src_uyvy, dst_y, width);
  }
789 790 791 792 793 794

#if defined(HAS_UYVYTOYROW_AVX2)
  if (clear) {
    __asm vzeroupper;
  }
#endif
795 796 797
  return 0;
}

798
// Convert ARGB to I420.
799
LIBYUV_API
800
int ARGBToI420(const uint8* src_argb, int src_stride_argb,
801 802 803 804
               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) {
805 806 807 808 809 810
  if (!src_argb ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
811 812
  if (height < 0) {
    height = -height;
813 814
    src_argb = src_argb + (height - 1) * src_stride_argb;
    src_stride_argb = -src_stride_argb;
815 816
  }
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
817 818 819
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
820
#if defined(HAS_ARGBTOYROW_SSSE3)
821 822 823
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
824
    if (IS_ALIGNED(width, 16)) {
825 826 827 828 829 830 831 832
      ARGBToUVRow = ARGBToUVRow_Unaligned_SSSE3;
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
      if (IS_ALIGNED(src_argb, 16) && IS_ALIGNED(src_stride_argb, 16)) {
        ARGBToUVRow = ARGBToUVRow_SSSE3;
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          ARGBToYRow = ARGBToYRow_SSSE3;
        }
      }
833
    }
834
  }
835 836 837 838 839 840 841 842
#endif
#if defined(HAS_ARGBTOYROW_AVX2)
  bool clear = false;
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    clear = true;
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
843 844
      ARGBToUVRow = ARGBToUVRow_AVX2;
      ARGBToYRow = ARGBToYRow_AVX2;
845 846 847 848
    }
  }
#endif
#if defined(HAS_ARGBTOYROW_NEON)
849 850
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    ARGBToYRow = ARGBToYRow_Any_NEON;
851 852
    if (IS_ALIGNED(width, 8)) {
      ARGBToYRow = ARGBToYRow_NEON;
853 854 855
    }
    if (width >= 16) {
      ARGBToUVRow = ARGBToUVRow_Any_NEON;
fbarchard@google.com's avatar
fbarchard@google.com committed
856 857 858
      if (IS_ALIGNED(width, 16)) {
        ARGBToUVRow = ARGBToUVRow_NEON;
      }
859 860
    }
  }
861
#endif
862 863 864 865 866 867 868 869 870

  for (int y = 0; y < height - 1; y += 2) {
    ARGBToUVRow(src_argb, src_stride_argb, dst_u, dst_v, width);
    ARGBToYRow(src_argb, dst_y, width);
    ARGBToYRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width);
    src_argb += src_stride_argb * 2;
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
871
  }
872 873 874 875
  if (height & 1) {
    ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
    ARGBToYRow(src_argb, dst_y, width);
  }
876 877 878 879 880 881

#if defined(HAS_ARGBTOYROW_AVX2)
  if (clear) {
    __asm vzeroupper;
  }
#endif
882 883 884
  return 0;
}

885
// Convert BGRA to I420.
886
LIBYUV_API
887 888 889 890 891
int 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) {
892 893 894 895 896 897
  if (!src_bgra ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
898 899 900 901 902 903
  if (height < 0) {
    height = -height;
    src_bgra = src_bgra + (height - 1) * src_stride_bgra;
    src_stride_bgra = -src_stride_bgra;
  }
  void (*BGRAToUVRow)(const uint8* src_bgra0, int src_stride_bgra,
904 905 906
                      uint8* dst_u, uint8* dst_v, int width) = BGRAToUVRow_C;
  void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int pix) =
      BGRAToYRow_C;
907
#if defined(HAS_BGRATOYROW_SSSE3)
908 909 910
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    BGRAToUVRow = BGRAToUVRow_Any_SSSE3;
    BGRAToYRow = BGRAToYRow_Any_SSSE3;
911
    if (IS_ALIGNED(width, 16)) {
912 913 914 915 916 917 918 919
      BGRAToUVRow = BGRAToUVRow_Unaligned_SSSE3;
      BGRAToYRow = BGRAToYRow_Unaligned_SSSE3;
      if (IS_ALIGNED(src_bgra, 16) && IS_ALIGNED(src_stride_bgra, 16)) {
        BGRAToUVRow = BGRAToUVRow_SSSE3;
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          BGRAToYRow = BGRAToYRow_SSSE3;
        }
      }
920
    }
921
  }
922 923 924 925 926 927
#elif defined(HAS_BGRATOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    BGRAToYRow = BGRAToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      BGRAToYRow = BGRAToYRow_NEON;
    }
928 929 930 931 932 933
    if (width >= 16) {
      BGRAToUVRow = BGRAToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        BGRAToUVRow = BGRAToUVRow_NEON;
      }
    }
934
  }
935
#endif
936

937 938 939 940 941
  for (int y = 0; y < height - 1; y += 2) {
    BGRAToUVRow(src_bgra, src_stride_bgra, dst_u, dst_v, width);
    BGRAToYRow(src_bgra, dst_y, width);
    BGRAToYRow(src_bgra + src_stride_bgra, dst_y + dst_stride_y, width);
    src_bgra += src_stride_bgra * 2;
942 943 944 945 946
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
947 948
    BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width);
    BGRAToYRow(src_bgra, dst_y, width);
949 950
  }
  return 0;
951 952
}

953
// Convert ABGR to I420.
954
LIBYUV_API
955
int ABGRToI420(const uint8* src_abgr, int src_stride_abgr,
956 957 958 959
               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) {
960 961 962 963 964 965
  if (!src_abgr ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
966 967
  if (height < 0) {
    height = -height;
968 969
    src_abgr = src_abgr + (height - 1) * src_stride_abgr;
    src_stride_abgr = -src_stride_abgr;
970
  }
971
  void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr,
972 973 974
                      uint8* dst_u, uint8* dst_v, int width) = ABGRToUVRow_C;
  void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int pix) =
      ABGRToYRow_C;
975
#if defined(HAS_ABGRTOYROW_SSSE3)
976 977 978
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
    ABGRToYRow = ABGRToYRow_Any_SSSE3;
979
    if (IS_ALIGNED(width, 16)) {
980 981 982 983 984 985 986 987
      ABGRToUVRow = ABGRToUVRow_Unaligned_SSSE3;
      ABGRToYRow = ABGRToYRow_Unaligned_SSSE3;
      if (IS_ALIGNED(src_abgr, 16) && IS_ALIGNED(src_stride_abgr, 16)) {
        ABGRToUVRow = ABGRToUVRow_SSSE3;
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          ABGRToYRow = ABGRToYRow_SSSE3;
        }
      }
988
    }
989
  }
990 991 992 993 994 995
#elif defined(HAS_ABGRTOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    ABGRToYRow = ABGRToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ABGRToYRow = ABGRToYRow_NEON;
    }
996 997 998 999 1000 1001
    if (width >= 16) {
      ABGRToUVRow = ABGRToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        ABGRToUVRow = ABGRToUVRow_NEON;
      }
    }
1002
  }
1003
#endif
1004

1005 1006 1007 1008 1009
  for (int y = 0; y < height - 1; y += 2) {
    ABGRToUVRow(src_abgr, src_stride_abgr, dst_u, dst_v, width);
    ABGRToYRow(src_abgr, dst_y, width);
    ABGRToYRow(src_abgr + src_stride_abgr, dst_y + dst_stride_y, width);
    src_abgr += src_stride_abgr * 2;
1010 1011 1012 1013 1014
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1015 1016
    ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
    ABGRToYRow(src_abgr, dst_y, width);
1017 1018
  }
  return 0;
1019 1020
}

1021
// Convert RGBA to I420.
1022
LIBYUV_API
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
int 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) {
  if (!src_rgba ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
    src_rgba = src_rgba + (height - 1) * src_stride_rgba;
    src_stride_rgba = -src_stride_rgba;
  }
  void (*RGBAToUVRow)(const uint8* src_rgba0, int src_stride_rgba,
1040 1041 1042
                      uint8* dst_u, uint8* dst_v, int width) = RGBAToUVRow_C;
  void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int pix) =
      RGBAToYRow_C;
1043
#if defined(HAS_RGBATOYROW_SSSE3)
1044 1045 1046
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    RGBAToUVRow = RGBAToUVRow_Any_SSSE3;
    RGBAToYRow = RGBAToYRow_Any_SSSE3;
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    if (IS_ALIGNED(width, 16)) {
      RGBAToUVRow = RGBAToUVRow_Unaligned_SSSE3;
      RGBAToYRow = RGBAToYRow_Unaligned_SSSE3;
      if (IS_ALIGNED(src_rgba, 16) && IS_ALIGNED(src_stride_rgba, 16)) {
        RGBAToUVRow = RGBAToUVRow_SSSE3;
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          RGBAToYRow = RGBAToYRow_SSSE3;
        }
      }
    }
  }
1058 1059 1060 1061 1062 1063
#elif defined(HAS_RGBATOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    RGBAToYRow = RGBAToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGBAToYRow = RGBAToYRow_NEON;
    }
1064 1065 1066 1067 1068 1069
    if (width >= 16) {
      RGBAToUVRow = RGBAToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        RGBAToUVRow = RGBAToUVRow_NEON;
      }
    }
1070
  }
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
#endif

  for (int y = 0; y < height - 1; y += 2) {
    RGBAToUVRow(src_rgba, src_stride_rgba, dst_u, dst_v, width);
    RGBAToYRow(src_rgba, dst_y, width);
    RGBAToYRow(src_rgba + src_stride_rgba, dst_y + dst_stride_y, width);
    src_rgba += src_stride_rgba * 2;
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
    RGBAToUVRow(src_rgba, 0, dst_u, dst_v, width);
    RGBAToYRow(src_rgba, dst_y, width);
  }
  return 0;
}

1089
// Convert RGB24 to I420.
1090
LIBYUV_API
1091
int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24,
1092 1093 1094 1095
                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) {
1096 1097 1098
  if (!src_rgb24 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1099 1100
    return -1;
  }
1101
  // Negative height means invert the image.
1102 1103
  if (height < 0) {
    height = -height;
1104 1105
    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
    src_stride_rgb24 = -src_stride_rgb24;
1106
  }
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125

#if defined(HAS_RGB24TOYROW_NEON)
  void (*RGB24ToUVRow)(const uint8* src_rgb24, int src_stride_rgb24,
      uint8* dst_u, uint8* dst_v, int width) = RGB24ToUVRow_C;
  void (*RGB24ToYRow)(const uint8* src_rgb24, uint8* dst_y, int pix) =
      RGB24ToYRow_C;
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    RGB24ToYRow = RGB24ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGB24ToYRow = RGB24ToYRow_NEON;
    }
    if (width >= 16) {
      RGB24ToUVRow = RGB24ToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        RGB24ToUVRow = RGB24ToUVRow_NEON;
      }
    }
  }
#else  // HAS_RGB24TOYROW_NEON
1126
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1127 1128
  void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      RGB24ToARGBRow_C;
1129
#if defined(HAS_RGB24TOARGBROW_SSSE3)
1130 1131 1132 1133 1134 1135
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    RGB24ToARGBRow = RGB24ToARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
    }
  }
1136 1137
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      ARGBToUVRow = ARGBToUVRow_SSSE3;
    }
  }
#endif
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
1151
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1152 1153
    if (IS_ALIGNED(width, 16)) {
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1154 1155 1156
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1157
    }
1158
  }
1159 1160
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_RGB24TOYROW_NEON
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
1161

1162
  for (int y = 0; y < height - 1; y += 2) {
1163
#if defined(HAS_RGB24TOYROW_NEON)
1164
    RGB24ToUVRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width);
1165 1166 1167
    RGB24ToYRow(src_rgb24, dst_y, width);
    RGB24ToYRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width);
#else
1168 1169 1170
    RGB24ToARGBRow(src_rgb24, row, width);
    RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kMaxStride, width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1171 1172
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1173
#endif
1174
    src_rgb24 += src_stride_rgb24 * 2;
1175 1176 1177 1178 1179
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1180
#if defined(HAS_RGB24TOYROW_NEON)
1181
    RGB24ToUVRow(src_rgb24, 0, dst_u, dst_v, width);
1182 1183
    RGB24ToYRow(src_rgb24, dst_y, width);
#else
1184 1185
    RGB24ToARGBRow(src_rgb24, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1186
    ARGBToYRow(row, dst_y, width);
1187
#endif
1188 1189
  }
  return 0;
1190 1191
}

1192
// Convert RAW to I420.
1193
LIBYUV_API
1194
int RAWToI420(const uint8* src_raw, int src_stride_raw,
1195 1196 1197 1198
              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) {
1199 1200 1201
  if (!src_raw || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1202 1203
    return -1;
  }
1204
  // Negative height means invert the image.
1205 1206
  if (height < 0) {
    height = -height;
1207 1208
    src_raw = src_raw + (height - 1) * src_stride_raw;
    src_stride_raw = -src_stride_raw;
1209
  }
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228

#if defined(HAS_RAWTOYROW_NEON)
  void (*RAWToUVRow)(const uint8* src_raw, int src_stride_raw,
      uint8* dst_u, uint8* dst_v, int width) = RAWToUVRow_C;
  void (*RAWToYRow)(const uint8* src_raw, uint8* dst_y, int pix) =
      RAWToYRow_C;
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    RAWToYRow = RAWToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RAWToYRow = RAWToYRow_NEON;
    }
    if (width >= 16) {
      RAWToUVRow = RAWToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        RAWToUVRow = RAWToUVRow_NEON;
      }
    }
  }
#else  // HAS_RAWTOYROW_NEON
1229
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1230 1231
  void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      RAWToARGBRow_C;
1232
#if defined(HAS_RAWTOARGBROW_SSSE3)
1233 1234 1235 1236 1237 1238
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    RAWToARGBRow = RAWToARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      RAWToARGBRow = RAWToARGBRow_SSSE3;
    }
  }
1239 1240
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      ARGBToUVRow = ARGBToUVRow_SSSE3;
    }
  }
#endif
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
1254
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1255 1256
    if (IS_ALIGNED(width, 16)) {
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1257 1258 1259
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1260
    }
1261
  }
1262 1263
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_RAWTOYROW_NEON
1264

1265
  for (int y = 0; y < height - 1; y += 2) {
1266
#if defined(HAS_RAWTOYROW_NEON)
1267
    RAWToUVRow(src_raw, src_stride_raw, dst_u, dst_v, width);
1268 1269 1270
    RAWToYRow(src_raw, dst_y, width);
    RAWToYRow(src_raw + src_stride_raw, dst_y + dst_stride_y, width);
#else
1271 1272 1273
    RAWToARGBRow(src_raw, row, width);
    RAWToARGBRow(src_raw + src_stride_raw, row + kMaxStride, width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1274 1275
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1276
#endif
1277
    src_raw += src_stride_raw * 2;
1278 1279 1280 1281 1282
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1283
#if defined(HAS_RAWTOYROW_NEON)
1284
    RAWToUVRow(src_raw, 0, dst_u, dst_v, width);
1285 1286
    RAWToYRow(src_raw, dst_y, width);
#else
1287 1288
    RAWToARGBRow(src_raw, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1289
    ARGBToYRow(row, dst_y, width);
1290
#endif
1291 1292
  }
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
1293 1294
}

1295
// Convert RGB565 to I420.
1296
LIBYUV_API
1297
int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565,
1298 1299 1300 1301 1302 1303 1304
                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) {
  if (!src_rgb565 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1305 1306
    return -1;
  }
1307
  // Negative height means invert the image.
1308 1309
  if (height < 0) {
    height = -height;
1310 1311
    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
    src_stride_rgb565 = -src_stride_rgb565;
1312
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331

#if defined(HAS_RGB565TOYROW_NEON)
  void (*RGB565ToUVRow)(const uint8* src_rgb565, int src_stride_rgb565,
      uint8* dst_u, uint8* dst_v, int width) = RGB565ToUVRow_C;
  void (*RGB565ToYRow)(const uint8* src_rgb565, uint8* dst_y, int pix) =
      RGB565ToYRow_C;
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    RGB565ToYRow = RGB565ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGB565ToYRow = RGB565ToYRow_NEON;
    }
    if (width >= 16) {
      RGB565ToUVRow = RGB565ToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        RGB565ToUVRow = RGB565ToUVRow_NEON;
      }
    }
  }
#else  // HAS_RGB565TOYROW_NEON
1332
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1333 1334
  void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      RGB565ToARGBRow_C;
1335
#if defined(HAS_RGB565TOARGBROW_SSE2)
1336 1337 1338 1339 1340 1341
  if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
    RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
    }
  }
1342 1343
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      ARGBToUVRow = ARGBToUVRow_SSSE3;
    }
  }
#endif
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
1357
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1358 1359
    if (IS_ALIGNED(width, 16)) {
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1360 1361 1362
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1363
    }
1364
  }
1365 1366
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_RGB565TOYROW_NEON
1367

1368
  for (int y = 0; y < height - 1; y += 2) {
1369
#if defined(HAS_RGB565TOYROW_NEON)
fbarchard@google.com's avatar
fbarchard@google.com committed
1370
    RGB565ToUVRow(src_rgb565, src_stride_rgb565, dst_u, dst_v, width);
1371 1372 1373
    RGB565ToYRow(src_rgb565, dst_y, width);
    RGB565ToYRow(src_rgb565 + src_stride_rgb565, dst_y + dst_stride_y, width);
#else
fbarchard@google.com's avatar
fbarchard@google.com committed
1374 1375 1376
    RGB565ToARGBRow(src_rgb565, row, width);
    RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kMaxStride, width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1377 1378
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1379
#endif
1380
    src_rgb565 += src_stride_rgb565 * 2;
1381 1382 1383 1384 1385
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1386
#if defined(HAS_RGB565TOYROW_NEON)
fbarchard@google.com's avatar
fbarchard@google.com committed
1387
    RGB565ToUVRow(src_rgb565, 0, dst_u, dst_v, width);
1388 1389
    RGB565ToYRow(src_rgb565, dst_y, width);
#else
fbarchard@google.com's avatar
fbarchard@google.com committed
1390 1391
    RGB565ToARGBRow(src_rgb565, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1392
    ARGBToYRow(row, dst_y, width);
1393
#endif
1394 1395 1396 1397
  }
  return 0;
}

1398
// Convert ARGB1555 to I420.
1399
LIBYUV_API
1400
int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555,
1401 1402 1403 1404
                   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) {
1405 1406 1407
  if (!src_argb1555 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1408 1409
    return -1;
  }
1410
  // Negative height means invert the image.
1411 1412
  if (height < 0) {
    height = -height;
1413 1414
    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
    src_stride_argb1555 = -src_stride_argb1555;
1415
  }
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434

#if defined(HAS_ARGB1555TOYROW_NEON)
  void (*ARGB1555ToUVRow)(const uint8* src_argb1555, int src_stride_argb1555,
      uint8* dst_u, uint8* dst_v, int width) = ARGB1555ToUVRow_C;
  void (*ARGB1555ToYRow)(const uint8* src_argb1555, uint8* dst_y, int pix) =
      ARGB1555ToYRow_C;
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    ARGB1555ToYRow = ARGB1555ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ARGB1555ToYRow = ARGB1555ToYRow_NEON;
    }
    if (width >= 16) {
      ARGB1555ToUVRow = ARGB1555ToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        ARGB1555ToUVRow = ARGB1555ToUVRow_NEON;
      }
    }
  }
#else  // HAS_ARGB1555TOYROW_NEON
1435
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1436 1437
  void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      ARGB1555ToARGBRow_C;
1438
#if defined(HAS_ARGB1555TOARGBROW_SSE2)
1439 1440 1441 1442 1443 1444
  if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
    }
  }
1445 1446
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1447 1448
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
1449 1450
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1451
    if (IS_ALIGNED(width, 16)) {
1452
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1453 1454 1455 1456 1457 1458 1459 1460 1461
    }
  }
#endif
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
1462
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1463 1464 1465
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1466
    }
1467
  }
1468 1469
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_ARGB1555TOYROW_NEON
1470

1471
  for (int y = 0; y < height - 1; y += 2) {
1472
#if defined(HAS_ARGB1555TOYROW_NEON)
1473
    ARGB1555ToUVRow(src_argb1555, src_stride_argb1555, dst_u, dst_v, width);
1474
    ARGB1555ToYRow(src_argb1555, dst_y, width);
1475 1476
    ARGB1555ToYRow(src_argb1555 + src_stride_argb1555, dst_y + dst_stride_y,
                   width);
1477
#else
1478
    ARGB1555ToARGBRow(src_argb1555, row, width);
1479 1480
    ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555, row + kMaxStride,
                      width);
1481
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1482 1483
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1484
#endif
1485
    src_argb1555 += src_stride_argb1555 * 2;
1486 1487 1488 1489 1490
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1491
#if defined(HAS_ARGB1555TOYROW_NEON)
1492
    ARGB1555ToUVRow(src_argb1555, 0, dst_u, dst_v, width);
1493 1494
    ARGB1555ToYRow(src_argb1555, dst_y, width);
#else
1495 1496
    ARGB1555ToARGBRow(src_argb1555, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1497
    ARGBToYRow(row, dst_y, width);
1498
#endif
1499 1500 1501 1502
  }
  return 0;
}

1503
// Convert ARGB4444 to I420.
1504
LIBYUV_API
1505
int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444,
1506 1507 1508 1509
                   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) {
1510 1511 1512
  if (!src_argb4444 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1513 1514
    return -1;
  }
1515
  // Negative height means invert the image.
1516 1517
  if (height < 0) {
    height = -height;
1518 1519
    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
    src_stride_argb4444 = -src_stride_argb4444;
1520
  }
1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539

#if defined(HAS_ARGB4444TOYROW_NEON)
  void (*ARGB4444ToUVRow)(const uint8* src_argb4444, int src_stride_argb4444,
      uint8* dst_u, uint8* dst_v, int width) = ARGB4444ToUVRow_C;
  void (*ARGB4444ToYRow)(const uint8* src_argb4444, uint8* dst_y, int pix) =
      ARGB4444ToYRow_C;
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    ARGB4444ToYRow = ARGB4444ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ARGB4444ToYRow = ARGB4444ToYRow_NEON;
    }
    if (width >= 16) {
      ARGB4444ToUVRow = ARGB4444ToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        ARGB4444ToUVRow = ARGB4444ToUVRow_NEON;
      }
    }
  }
#else  // HAS_ARGB4444TOYROW_NEON
1540
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1541 1542
  void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      ARGB4444ToARGBRow_C;
1543
#if defined(HAS_ARGB4444TOARGBROW_SSE2)
1544 1545 1546 1547 1548 1549
  if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
    }
  }
1550 1551
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1552 1553
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
1554 1555
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1556
    if (IS_ALIGNED(width, 16)) {
1557
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1558 1559 1560 1561 1562 1563 1564 1565 1566
    }
  }
#endif
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
1567
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1568 1569 1570
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1571
    }
1572
  }
1573 1574
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_ARGB4444TOYROW_NEON
1575

1576
  for (int y = 0; y < height - 1; y += 2) {
1577
#if defined(HAS_ARGB4444TOYROW_NEON)
1578
    ARGB4444ToUVRow(src_argb4444, src_stride_argb4444, dst_u, dst_v, width);
1579
    ARGB4444ToYRow(src_argb4444, dst_y, width);
1580 1581
    ARGB4444ToYRow(src_argb4444 + src_stride_argb4444, dst_y + dst_stride_y,
                   width);
1582
#else
1583 1584 1585 1586
    ARGB4444ToARGBRow(src_argb4444, row, width);
    ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444, row + kMaxStride,
                      width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1587 1588
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1589
#endif
1590
    src_argb4444 += src_stride_argb4444 * 2;
1591 1592 1593 1594 1595
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1596
#if defined(HAS_ARGB4444TOYROW_NEON)
1597
    ARGB4444ToUVRow(src_argb4444, 0, dst_u, dst_v, width);
1598 1599
    ARGB4444ToYRow(src_argb4444, dst_y, width);
#else
1600 1601
    ARGB4444ToARGBRow(src_argb4444, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1602
    ARGBToYRow(row, dst_y, width);
1603
#endif
1604 1605 1606 1607
  }
  return 0;
}

fbarchard@google.com's avatar
fbarchard@google.com committed
1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620
#ifdef HAVE_JPEG
struct I420Buffers {
  uint8* y;
  int y_stride;
  uint8* u;
  int u_stride;
  uint8* v;
  int v_stride;
  int w;
  int h;
};

static void JpegCopyI420(void* opaque,
1621
                         const uint8* const* data,
fbarchard@google.com's avatar
fbarchard@google.com committed
1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
                         const int* strides,
                         int rows) {
  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
  I420Copy(data[0], strides[0],
           data[1], strides[1],
           data[2], strides[2],
           dest->y, dest->y_stride,
           dest->u, dest->u_stride,
           dest->v, dest->v_stride,
           dest->w, rows);
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI422ToI420(void* opaque,
1639
                           const uint8* const* data,
fbarchard@google.com's avatar
fbarchard@google.com committed
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656
                           const int* strides,
                           int rows) {
  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
  I422ToI420(data[0], strides[0],
             data[1], strides[1],
             data[2], strides[2],
             dest->y, dest->y_stride,
             dest->u, dest->u_stride,
             dest->v, dest->v_stride,
             dest->w, rows);
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI444ToI420(void* opaque,
1657
                           const uint8* const* data,
fbarchard@google.com's avatar
fbarchard@google.com committed
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
                           const int* strides,
                           int rows) {
  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
  I444ToI420(data[0], strides[0],
             data[1], strides[1],
             data[2], strides[2],
             dest->y, dest->y_stride,
             dest->u, dest->u_stride,
             dest->v, dest->v_stride,
             dest->w, rows);
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI411ToI420(void* opaque,
1675
                           const uint8* const* data,
fbarchard@google.com's avatar
fbarchard@google.com committed
1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
                           const int* strides,
                           int rows) {
  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
  I411ToI420(data[0], strides[0],
             data[1], strides[1],
             data[2], strides[2],
             dest->y, dest->y_stride,
             dest->u, dest->u_stride,
             dest->v, dest->v_stride,
             dest->w, rows);
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

static void JpegI400ToI420(void* opaque,
1693
                           const uint8* const* data,
fbarchard@google.com's avatar
fbarchard@google.com committed
1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707
                           const int* strides,
                           int rows) {
  I420Buffers* dest = static_cast<I420Buffers*>(opaque);
  I400ToI420(data[0], strides[0],
             dest->y, dest->y_stride,
             dest->u, dest->u_stride,
             dest->v, dest->v_stride,
             dest->w, rows);
  dest->y += rows * dest->y_stride;
  dest->u += ((rows + 1) >> 1) * dest->u_stride;
  dest->v += ((rows + 1) >> 1) * dest->v_stride;
  dest->h -= rows;
}

1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
// Query size of MJPG in pixels.
LIBYUV_API
int MJPGSize(const uint8* sample, size_t sample_size,
             int* width, int* height) {
  MJpegDecoder mjpeg_decoder;
  bool ret = mjpeg_decoder.LoadFrame(sample, sample_size);
  if (ret) {
    *width = mjpeg_decoder.GetWidth();
    *height = mjpeg_decoder.GetHeight();
  }
  mjpeg_decoder.UnloadFrame();
  return ret ? 0 : -1;  // -1 for runtime failure.
}

fbarchard@google.com's avatar
fbarchard@google.com committed
1722
// MJPG (Motion JPeg) to I420
1723
// TODO(fbarchard): review w and h requirement. dw and dh may be enough.
1724
LIBYUV_API
fbarchard@google.com's avatar
fbarchard@google.com committed
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736
int 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) {
  if (sample_size == kUnknownDataSize) {
    // ERROR: MJPEG frame size unknown
    return -1;
  }

1737
  // TODO(fbarchard): Port MJpeg to C.
1738 1739 1740 1741
  MJpegDecoder mjpeg_decoder;
  bool ret = mjpeg_decoder.LoadFrame(sample, sample_size);
  if (ret && (mjpeg_decoder.GetWidth() != w ||
              mjpeg_decoder.GetHeight() != h)) {
fbarchard@google.com's avatar
fbarchard@google.com committed
1742
    // ERROR: MJPEG frame has unexpected dimensions
1743
    mjpeg_decoder.UnloadFrame();
fbarchard@google.com's avatar
fbarchard@google.com committed
1744 1745 1746 1747 1748
    return 1;  // runtime failure
  }
  if (ret) {
    I420Buffers bufs = { y, y_stride, u, u_stride, v, v_stride, dw, dh };
    // YUV420
1749
    if (mjpeg_decoder.GetColorSpace() ==
fbarchard@google.com's avatar
fbarchard@google.com committed
1750
            MJpegDecoder::kColorSpaceYCbCr &&
1751 1752 1753 1754 1755 1756 1757 1758
        mjpeg_decoder.GetNumComponents() == 3 &&
        mjpeg_decoder.GetVertSampFactor(0) == 2 &&
        mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
        mjpeg_decoder.GetVertSampFactor(1) == 1 &&
        mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
        mjpeg_decoder.GetVertSampFactor(2) == 1 &&
        mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegCopyI420, &bufs, dw, dh);
fbarchard@google.com's avatar
fbarchard@google.com committed
1759
    // YUV422
1760
    } else if (mjpeg_decoder.GetColorSpace() ==
fbarchard@google.com's avatar
fbarchard@google.com committed
1761
                   MJpegDecoder::kColorSpaceYCbCr &&
1762 1763 1764 1765 1766 1767 1768 1769
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 2 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI422ToI420, &bufs, dw, dh);
fbarchard@google.com's avatar
fbarchard@google.com committed
1770
    // YUV444
1771
    } else if (mjpeg_decoder.GetColorSpace() ==
fbarchard@google.com's avatar
fbarchard@google.com committed
1772
                   MJpegDecoder::kColorSpaceYCbCr &&
1773 1774 1775 1776 1777 1778 1779 1780
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 1 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI444ToI420, &bufs, dw, dh);
fbarchard@google.com's avatar
fbarchard@google.com committed
1781
    // YUV411
1782
    } else if (mjpeg_decoder.GetColorSpace() ==
fbarchard@google.com's avatar
fbarchard@google.com committed
1783
                   MJpegDecoder::kColorSpaceYCbCr &&
1784 1785 1786 1787 1788 1789 1790 1791
               mjpeg_decoder.GetNumComponents() == 3 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 4 &&
               mjpeg_decoder.GetVertSampFactor(1) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(1) == 1 &&
               mjpeg_decoder.GetVertSampFactor(2) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(2) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI411ToI420, &bufs, dw, dh);
fbarchard@google.com's avatar
fbarchard@google.com committed
1792
    // YUV400
1793
    } else if (mjpeg_decoder.GetColorSpace() ==
fbarchard@google.com's avatar
fbarchard@google.com committed
1794
                   MJpegDecoder::kColorSpaceGrayscale &&
1795 1796 1797 1798
               mjpeg_decoder.GetNumComponents() == 1 &&
               mjpeg_decoder.GetVertSampFactor(0) == 1 &&
               mjpeg_decoder.GetHorizSampFactor(0) == 1) {
      ret = mjpeg_decoder.DecodeToCallback(&JpegI400ToI420, &bufs, dw, dh);
fbarchard@google.com's avatar
fbarchard@google.com committed
1799 1800
    } else {
      // TODO(fbarchard): Implement conversion for any other colorspace/sample
1801
      // factors that occur in practice. 411 is supported by libjpeg
fbarchard@google.com's avatar
fbarchard@google.com committed
1802
      // ERROR: Unable to convert MJPEG frame because format is not supported
1803
      mjpeg_decoder.UnloadFrame();
fbarchard@google.com's avatar
fbarchard@google.com committed
1804 1805 1806 1807 1808 1809 1810
      return 1;
    }
  }
  return 0;
}
#endif

1811
// Convert camera sample to I420 with cropping, rotation and vertical flip.
1812 1813
// src_width is used for source stride computation
// src_height is used to compute location of planes, and indicate inversion
fbarchard@google.com's avatar
fbarchard@google.com committed
1814 1815
// sample_size is measured in bytes and is the size of the frame.
//   With MJPEG it is the compressed size of the frame.
1816
LIBYUV_API
1817 1818 1819 1820 1821 1822
int ConvertToI420(const uint8* sample,
#ifdef HAVE_JPEG
                  size_t sample_size,
#else
                  size_t /* sample_size */,
#endif
1823 1824 1825
                  uint8* y, int y_stride,
                  uint8* u, int u_stride,
                  uint8* v, int v_stride,
1826 1827 1828
                  int crop_x, int crop_y,
                  int src_width, int src_height,
                  int dst_width, int dst_height,
1829
                  RotationMode rotation,
1830 1831
                  uint32 fourcc) {
  uint32 format = CanonicalFourCC(fourcc);
1832 1833 1834
  if (!y || !u || !v || !sample ||
      src_width <= 0 || dst_width <= 0  ||
      src_height == 0 || dst_height == 0) {
1835 1836 1837
    return -1;
  }
  int aligned_src_width = (src_width + 1) & ~1;
1838 1839
  const uint8* src;
  const uint8* src_uv;
1840 1841 1842 1843 1844
  int abs_src_height = (src_height < 0) ? -src_height : src_height;
  int inv_dst_height = (dst_height < 0) ? -dst_height : dst_height;
  if (src_height < 0) {
    inv_dst_height = -inv_dst_height;
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
1845
  int r = 0;
1846

1847
  // One pass rotation is available for some formats. For the rest, convert
1848 1849 1850 1851
  // to I420 (with optional vertical flipping) into a temporary I420 buffer,
  // and then rotate the I420 to the final destination buffer.
  // For in-place conversion, if destination y is same as source sample,
  // also enable temporary buffer.
1852 1853 1854
  bool need_buf = (rotation && format != FOURCC_I420 &&
      format != FOURCC_NV12 && format != FOURCC_NV21 &&
      format != FOURCC_YU12 && format != FOURCC_YV12) || y == sample;
1855 1856 1857 1858 1859 1860
  uint8* tmp_y = y;
  uint8* tmp_u = u;
  uint8* tmp_v = v;
  int tmp_y_stride = y_stride;
  int tmp_u_stride = u_stride;
  int tmp_v_stride = v_stride;
fbarchard@google.com's avatar
fbarchard@google.com committed
1861
  uint8* buf = NULL;
1862
  int abs_dst_height = (dst_height < 0) ? -dst_height : dst_height;
1863
  if (need_buf) {
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876
    int y_size = dst_width * abs_dst_height;
    int uv_size = ((dst_width + 1) / 2) * ((abs_dst_height + 1) / 2);
    buf = new uint8[y_size + uv_size * 2];
    if (!buf) {
      return 1;  // Out of memory runtime error.
    }
    y = buf;
    u = y + y_size;
    v = u + uv_size;
    y_stride = dst_width;
    u_stride = v_stride = ((dst_width + 1) / 2);
  }

1877 1878 1879
  switch (format) {
    // Single plane formats
    case FOURCC_YUY2:
fbarchard@google.com's avatar
fbarchard@google.com committed
1880
      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
fbarchard@google.com's avatar
fbarchard@google.com committed
1881 1882 1883 1884 1885
      r = YUY2ToI420(src, aligned_src_width * 2,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
1886 1887
      break;
    case FOURCC_UYVY:
1888
      src = sample + (aligned_src_width * crop_y + crop_x) * 2;
fbarchard@google.com's avatar
fbarchard@google.com committed
1889 1890 1891 1892 1893
      r = UYVYToI420(src, aligned_src_width * 2,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
1894
      break;
1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918
    case FOURCC_RGBP:
      src = sample + (src_width * crop_y + crop_x) * 2;
      r = RGB565ToI420(src, src_width * 2,
                       y, y_stride,
                       u, u_stride,
                       v, v_stride,
                       dst_width, inv_dst_height);
      break;
    case FOURCC_RGBO:
      src = sample + (src_width * crop_y + crop_x) * 2;
      r = ARGB1555ToI420(src, src_width * 2,
                         y, y_stride,
                         u, u_stride,
                         v, v_stride,
                         dst_width, inv_dst_height);
      break;
    case FOURCC_R444:
      src = sample + (src_width * crop_y + crop_x) * 2;
      r = ARGB4444ToI420(src, src_width * 2,
                         y, y_stride,
                         u, u_stride,
                         v, v_stride,
                         dst_width, inv_dst_height);
      break;
1919
    case FOURCC_24BG:
1920
      src = sample + (src_width * crop_y + crop_x) * 3;
fbarchard@google.com's avatar
fbarchard@google.com committed
1921 1922 1923 1924 1925
      r = RGB24ToI420(src, src_width * 3,
                      y, y_stride,
                      u, u_stride,
                      v, v_stride,
                      dst_width, inv_dst_height);
1926 1927
      break;
    case FOURCC_RAW:
1928
      src = sample + (src_width * crop_y + crop_x) * 3;
fbarchard@google.com's avatar
fbarchard@google.com committed
1929 1930 1931 1932 1933
      r = RAWToI420(src, src_width * 3,
                    y, y_stride,
                    u, u_stride,
                    v, v_stride,
                    dst_width, inv_dst_height);
1934 1935
      break;
    case FOURCC_ARGB:
1936
      src = sample + (src_width * crop_y + crop_x) * 4;
fbarchard@google.com's avatar
fbarchard@google.com committed
1937 1938 1939 1940 1941
      r = ARGBToI420(src, src_width * 4,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
1942 1943
      break;
    case FOURCC_BGRA:
1944
      src = sample + (src_width * crop_y + crop_x) * 4;
fbarchard@google.com's avatar
fbarchard@google.com committed
1945 1946 1947 1948 1949
      r = BGRAToI420(src, src_width * 4,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
1950 1951
      break;
    case FOURCC_ABGR:
1952
      src = sample + (src_width * crop_y + crop_x) * 4;
fbarchard@google.com's avatar
fbarchard@google.com committed
1953 1954 1955 1956 1957
      r = ABGRToI420(src, src_width * 4,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
1958
      break;
1959 1960 1961 1962 1963 1964 1965 1966
    case FOURCC_RGBA:
      src = sample + (src_width * crop_y + crop_x) * 4;
      r = RGBAToI420(src, src_width * 4,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
      break;
1967 1968
    // TODO(fbarchard): Support cropping Bayer by odd numbers
    // by adjusting fourcc.
1969
    case FOURCC_BGGR:
1970
      src = sample + (src_width * crop_y + crop_x);
fbarchard@google.com's avatar
fbarchard@google.com committed
1971 1972 1973 1974 1975
      r = BayerBGGRToI420(src, src_width,
                          y, y_stride,
                          u, u_stride,
                          v, v_stride,
                          dst_width, inv_dst_height);
1976
      break;
1977
    case FOURCC_GBRG:
1978
      src = sample + (src_width * crop_y + crop_x);
fbarchard@google.com's avatar
fbarchard@google.com committed
1979 1980 1981 1982 1983
      r = BayerGBRGToI420(src, src_width,
                          y, y_stride,
                          u, u_stride,
                          v, v_stride,
                          dst_width, inv_dst_height);
1984
      break;
1985 1986
    case FOURCC_GRBG:
      src = sample + (src_width * crop_y + crop_x);
fbarchard@google.com's avatar
fbarchard@google.com committed
1987 1988 1989 1990 1991
      r = BayerGRBGToI420(src, src_width,
                          y, y_stride,
                          u, u_stride,
                          v, v_stride,
                          dst_width, inv_dst_height);
1992 1993 1994
      break;
    case FOURCC_RGGB:
      src = sample + (src_width * crop_y + crop_x);
fbarchard@google.com's avatar
fbarchard@google.com committed
1995 1996 1997 1998 1999
      r = BayerRGGBToI420(src, src_width,
                          y, y_stride,
                          u, u_stride,
                          v, v_stride,
                          dst_width, inv_dst_height);
2000
      break;
2001 2002
    case FOURCC_I400:
      src = sample + src_width * crop_y + crop_x;
fbarchard@google.com's avatar
fbarchard@google.com committed
2003 2004 2005 2006 2007
      r = I400ToI420(src, src_width,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
2008
      break;
2009 2010
    // Biplanar formats
    case FOURCC_NV12:
2011 2012
      src = sample + (src_width * crop_y + crop_x);
      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
fbarchard@google.com's avatar
fbarchard@google.com committed
2013 2014 2015 2016 2017 2018
      r = NV12ToI420Rotate(src, src_width,
                           src_uv, aligned_src_width,
                           y, y_stride,
                           u, u_stride,
                           v, v_stride,
                           dst_width, inv_dst_height, rotation);
2019 2020
      break;
    case FOURCC_NV21:
2021 2022
      src = sample + (src_width * crop_y + crop_x);
      src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x;
2023
      // Call NV12 but with u and v parameters swapped.
fbarchard@google.com's avatar
fbarchard@google.com committed
2024 2025 2026 2027
      r = NV12ToI420Rotate(src, src_width,
                           src_uv, aligned_src_width,
                           y, y_stride,
                           v, v_stride,
2028
                           u, u_stride,
fbarchard@google.com's avatar
fbarchard@google.com committed
2029
                           dst_width, inv_dst_height, rotation);
2030
      break;
2031 2032
    case FOURCC_M420:
      src = sample + (src_width * crop_y) * 12 / 8 + crop_x;
fbarchard@google.com's avatar
fbarchard@google.com committed
2033 2034 2035 2036 2037
      r = M420ToI420(src, src_width,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
2038
      break;
2039
    case FOURCC_Q420:
2040 2041 2042
      src = sample + (src_width + aligned_src_width * 2) * crop_y + crop_x;
      src_uv = sample + (src_width + aligned_src_width * 2) * crop_y +
               src_width + crop_x * 2;
fbarchard@google.com's avatar
fbarchard@google.com committed
2043 2044 2045 2046 2047 2048
      r = Q420ToI420(src, src_width * 3,
                    src_uv, src_width * 3,
                    y, y_stride,
                    u, u_stride,
                    v, v_stride,
                    dst_width, inv_dst_height);
2049 2050 2051
      break;
    // Triplanar formats
    case FOURCC_I420:
2052
    case FOURCC_YU12:
2053
    case FOURCC_YV12: {
2054
      const uint8* src_y = sample + (src_width * crop_y + crop_x);
2055 2056
      const uint8* src_u;
      const uint8* src_v;
2057 2058
      int halfwidth = (src_width + 1) / 2;
      int halfheight = (abs_src_height + 1) / 2;
2059
      if (format == FOURCC_YV12) {
2060
        src_v = sample + src_width * abs_src_height +
2061 2062
            (halfwidth * crop_y + crop_x) / 2;
        src_u = sample + src_width * abs_src_height +
2063
            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
2064
      } else {
2065
        src_u = sample + src_width * abs_src_height +
2066 2067
            (halfwidth * crop_y + crop_x) / 2;
        src_v = sample + src_width * abs_src_height +
2068
            halfwidth * (halfheight + crop_y / 2) + crop_x / 2;
2069
      }
fbarchard@google.com's avatar
fbarchard@google.com committed
2070 2071 2072 2073 2074 2075 2076
      r = I420Rotate(src_y, src_width,
                     src_u, halfwidth,
                     src_v, halfwidth,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height, rotation);
2077 2078
      break;
    }
2079 2080
    case FOURCC_I422:
    case FOURCC_YV16: {
2081
      const uint8* src_y = sample + src_width * crop_y + crop_x;
2082 2083 2084
      const uint8* src_u;
      const uint8* src_v;
      int halfwidth = (src_width + 1) / 2;
2085
      if (format == FOURCC_YV16) {
2086
        src_v = sample + src_width * abs_src_height +
2087 2088
            halfwidth * crop_y + crop_x / 2;
        src_u = sample + src_width * abs_src_height +
2089 2090 2091
            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
      } else {
        src_u = sample + src_width * abs_src_height +
2092 2093
            halfwidth * crop_y + crop_x / 2;
        src_v = sample + src_width * abs_src_height +
2094 2095
            halfwidth * (abs_src_height + crop_y) + crop_x / 2;
      }
fbarchard@google.com's avatar
fbarchard@google.com committed
2096 2097 2098 2099 2100 2101 2102
      r = I422ToI420(src_y, src_width,
                     src_u, halfwidth,
                     src_v, halfwidth,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
2103 2104
      break;
    }
2105 2106 2107 2108 2109
    case FOURCC_I444:
    case FOURCC_YV24: {
      const uint8* src_y = sample + src_width * crop_y + crop_x;
      const uint8* src_u;
      const uint8* src_v;
2110
      if (format == FOURCC_YV24) {
2111 2112
        src_v = sample + src_width * (abs_src_height + crop_y) + crop_x;
        src_u = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
2113 2114 2115
      } else {
        src_u = sample + src_width * (abs_src_height + crop_y) + crop_x;
        src_v = sample + src_width * (abs_src_height * 2 + crop_y) + crop_x;
2116
      }
fbarchard@google.com's avatar
fbarchard@google.com committed
2117 2118 2119 2120 2121 2122 2123
      r = I444ToI420(src_y, src_width,
                     src_u, src_width,
                     src_v, src_width,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
2124 2125
      break;
    }
2126 2127 2128 2129 2130 2131 2132
    case FOURCC_I411: {
      int quarterwidth = (src_width + 3) / 4;
      const uint8* src_y = sample + src_width * crop_y + crop_x;
      const uint8* src_u = sample + src_width * abs_src_height +
          quarterwidth * crop_y + crop_x / 4;
      const uint8* src_v = sample + src_width * abs_src_height +
          quarterwidth * (abs_src_height + crop_y) + crop_x / 4;
fbarchard@google.com's avatar
fbarchard@google.com committed
2133 2134 2135 2136 2137 2138 2139
      r = I411ToI420(src_y, src_width,
                     src_u, quarterwidth,
                     src_v, quarterwidth,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     dst_width, inv_dst_height);
2140 2141
      break;
    }
fbarchard@google.com's avatar
fbarchard@google.com committed
2142
#ifdef HAVE_JPEG
2143
    case FOURCC_MJPG:
fbarchard@google.com's avatar
fbarchard@google.com committed
2144 2145 2146 2147 2148 2149
      r = MJPGToI420(sample, sample_size,
                     y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     src_width, abs_src_height, dst_width, inv_dst_height);
      break;
fbarchard@google.com's avatar
fbarchard@google.com committed
2150
#endif
2151
    default:
2152 2153 2154
      r = -1;  // unknown fourcc - return failure code.
  }

2155
  if (need_buf) {
2156 2157 2158 2159 2160 2161 2162 2163 2164 2165
    if (!r) {
      r = I420Rotate(y, y_stride,
                     u, u_stride,
                     v, v_stride,
                     tmp_y, tmp_y_stride,
                     tmp_u, tmp_u_stride,
                     tmp_v, tmp_v_stride,
                     dst_width, abs_dst_height, rotation);
    }
    delete buf;
2166
  }
2167

fbarchard@google.com's avatar
fbarchard@google.com committed
2168
  return r;
2169 2170
}

2171 2172 2173 2174
#ifdef __cplusplus
}  // extern "C"
}  // namespace libyuv
#endif