convert.cc 50.2 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 16
#include "libyuv/planar_functions.h"
#include "libyuv/rotate.h"
17
#include "libyuv/scale.h"  // For ScalePlane()
18
#include "libyuv/row.h"
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
19

20
#ifdef __cplusplus
21
namespace libyuv {
22 23
extern "C" {
#endif
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
24

25
// Copy I420 with optional flipping
26
LIBYUV_API
27 28 29 30 31 32 33 34 35 36 37
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;
38
  }
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
  // 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;
59
}
60

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

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
  // 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);
131 132 133
  }
  return 0;
}
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
134

135 136
// Blends 32x2 pixels to 16x1
// source in scale.cc
137 138
#if !defined(LIBYUV_DISABLE_NEON) && \
    (defined(__ARM_NEON__) || defined(LIBYUV_NEON))
139
#define HAS_SCALEROWDOWN2_NEON
140
void ScaleRowDown2Box_NEON(const uint8* src_ptr, ptrdiff_t src_stride,
141
                           uint8* dst, int dst_width);
142
#elif !defined(LIBYUV_DISABLE_X86) && \
143 144
    (defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))

145
void ScaleRowDown2Box_SSE2(const uint8* src_ptr, ptrdiff_t src_stride,
146 147
                           uint8* dst_ptr, int dst_width);
#endif
148
void ScaleRowDown2Box_C(const uint8* src_ptr, ptrdiff_t src_stride,
149 150
                        uint8* dst_ptr, int dst_width);

151
LIBYUV_API
152
int I444ToI420(const uint8* src_y, int src_stride_y,
153 154
               const uint8* src_u, int src_stride_u,
               const uint8* src_v, int src_stride_v,
155 156 157
               uint8* dst_y, int dst_stride_y,
               uint8* dst_u, int dst_stride_u,
               uint8* dst_v, int dst_stride_v,
158
               int width, int height) {
159 160 161 162 163
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
164 165 166
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
167 168 169 170 171 172 173 174
    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;
175
  void (*ScaleRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride,
176
                        uint8* dst_ptr, int dst_width) = ScaleRowDown2Box_C;
177 178 179
#if defined(HAS_SCALEROWDOWN2_NEON)
  if (TestCpuFlag(kCpuHasNEON) &&
      IS_ALIGNED(halfwidth, 16)) {
180
    ScaleRowDown2 = ScaleRowDown2Box_NEON;
fbarchard@google.com's avatar
fbarchard@google.com committed
181 182
  }
#elif defined(HAS_SCALEROWDOWN2_SSE2)
183
  if (TestCpuFlag(kCpuHasSSE2) &&
184 185 186 187 188
      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)) {
189
    ScaleRowDown2 = ScaleRowDown2Box_SSE2;
190
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
191
#endif
192

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
  // 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;
214 215
  }
  if (height & 1) {
216
    ScaleRowDown2(src_v, 0, dst_v, halfwidth);
217
  }
218
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
219 220
}

221
// TODO(fbarchard): Enable bilinear when fast enough or specialized upsampler.
222 223
// 411 chroma is 1/4 width, 1x height
// 420 chroma is 1/2 width, 1/2 height
224
LIBYUV_API
225 226 227 228 229 230 231
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) {
232 233 234 235 236
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
237 238 239
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
240 241 242 243 244 245
    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;
246 247 248 249 250 251 252 253 254 255 256
  }

  // 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;

257 258 259 260
  // 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);
261 262

  // Resample V plane.
263 264 265
  ScalePlane(src_v, src_stride_v, quarterwidth, height,
             dst_v, dst_stride_v, halfwidth, halfheight,
             kFilterNone);
266 267 268
  return 0;
}

269
// I400 is greyscale typically used in MJPG
270
LIBYUV_API
271 272 273 274
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,
275
               int width, int height) {
276 277 278 279
  if (!src_y || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
280 281 282
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
283 284 285 286 287 288 289 290
    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);
291 292 293
  return 0;
}

294
static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1,
295
                       uint8* dst, int dst_stride,
296
                       int width, int height) {
297
  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
298 299
#if defined(HAS_COPYROW_X86)
  if (TestCpuFlag(kCpuHasX86) && IS_ALIGNED(width, 4)) {
300
    CopyRow = CopyRow_X86;
301 302
  }
#endif
303
#if defined(HAS_COPYROW_SSE2)
304 305 306 307 308 309
  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;
  }
310
#endif
311
#if defined(HAS_COPYROW_ERMS)
312
  // TODO(fbarchard): Detect Fast String support.
313 314
  if (TestCpuFlag(kCpuHasERMS)) {
    CopyRow = CopyRow_ERMS;
315 316 317 318 319
  }
#endif
#if defined(HAS_COPYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
    CopyRow = CopyRow_NEON;
320 321
  }
#endif
322 323 324 325 326
#if defined(HAS_COPYROW_MIPS)
  if (TestCpuFlag(kCpuHasMIPS)) {
    CopyRow = CopyRow_MIPS;
  }
#endif
327

328
  // Copy plane
329 330
  for (int y = 0; y < height - 1; y += 2) {
    CopyRow(src, dst, width);
331
    CopyRow(src + src_stride_0, dst + dst_stride, width);
332
    src += src_stride_0 + src_stride_1;
333
    dst += dst_stride * 2;
334 335 336
  }
  if (height & 1) {
    CopyRow(src, dst, width);
337
  }
338 339 340 341 342 343
}

// 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:
344
// M420 is row biplanar 420: 2 rows of Y and 1 row of UV.
345
// Chroma is half width / half height. (420)
346
// src_stride_m420 is row planar. Normally this will be the width in pixels.
347 348 349 350 351 352 353 354 355
//   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) {
356 357 358 359 360
  if (!src_y || !src_uv ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
361 362 363
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
364 365 366 367 368 369 370 371
    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;
  }
372
  // Coalesce rows.
373
  int halfwidth = (width + 1) >> 1;
374 375 376 377
  int halfheight = (height + 1) >> 1;
  if (src_stride_y0 == width &&
      src_stride_y1 == width &&
      dst_stride_y == width) {
378
    width *= height;
379
    height = 1;
380
    src_stride_y0 = src_stride_y1 = dst_stride_y = 0;
381
  }
382 383
  // Coalesce rows.
  if (src_stride_uv == halfwidth * 2 &&
384 385
      dst_stride_u == halfwidth &&
      dst_stride_v == halfwidth) {
386
    halfwidth *= halfheight;
387
    halfheight = 1;
388
    src_stride_uv = dst_stride_u = dst_stride_v = 0;
389
  }
390 391 392
  void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v, int pix) =
      SplitUVRow_C;
#if defined(HAS_SPLITUVROW_SSE2)
393
  if (TestCpuFlag(kCpuHasSSE2) && halfwidth >= 16) {
394
    SplitUVRow = SplitUVRow_Any_SSE2;
395
    if (IS_ALIGNED(halfwidth, 16)) {
396
      SplitUVRow = SplitUVRow_Unaligned_SSE2;
397 398 399
      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)) {
400
        SplitUVRow = SplitUVRow_SSE2;
401 402
      }
    }
fbarchard@google.com's avatar
fbarchard@google.com committed
403
  }
404
#endif
405
#if defined(HAS_SPLITUVROW_AVX2)
406
  if (TestCpuFlag(kCpuHasAVX2) && halfwidth >= 32) {
407
    SplitUVRow = SplitUVRow_Any_AVX2;
408
    if (IS_ALIGNED(halfwidth, 32)) {
409
      SplitUVRow = SplitUVRow_AVX2;
410
    }
411
  }
412
#endif
413
#if defined(HAS_SPLITUVROW_NEON)
414
  if (TestCpuFlag(kCpuHasNEON) && halfwidth >= 16) {
415
    SplitUVRow = SplitUVRow_Any_NEON;
416
    if (IS_ALIGNED(halfwidth, 16)) {
417
      SplitUVRow = SplitUVRow_NEON;
418 419 420
    }
  }
#endif
421
#if defined(HAS_SPLITUVROW_MIPS_DSPR2)
422
  if (TestCpuFlag(kCpuHasMIPS_DSPR2) && halfwidth >= 16) {
423
    SplitUVRow = SplitUVRow_Any_MIPS_DSPR2;
424
    if (IS_ALIGNED(halfwidth, 16)) {
425
      SplitUVRow = SplitUVRow_Unaligned_MIPS_DSPR2;
426 427 428
      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)) {
429
        SplitUVRow = SplitUVRow_MIPS_DSPR2;
430 431
      }
    }
432
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
433
#endif
434

435
  if (dst_y) {
436 437 438 439 440 441
    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);
    }
442
  }
443 444 445

  for (int y = 0; y < halfheight; ++y) {
    // Copy a row of UV.
446
    SplitUVRow(src_uv, dst_u, dst_v, halfwidth);
447 448 449
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
    src_uv += src_stride_uv;
450 451 452 453
  }
  return 0;
}

454
// Convert NV12 to I420.
455
LIBYUV_API
456 457 458 459 460
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,
461
               int width, int height) {
462 463 464 465 466 467 468 469
  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);
}

470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
// 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);
}

486
// Convert M420 to I420.
487
LIBYUV_API
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
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
503
LIBYUV_API
504 505 506 507 508 509
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) {
510 511 512 513 514
  if (!src_y || !src_yuy2 ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
515 516 517
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
518 519 520 521 522 523 524 525
    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;
  }
526
  // CopyRow for rows of just Y in Q420 copied to Y plane of I420.
527 528
  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
#if defined(HAS_COPYROW_NEON)
529
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
530 531
    CopyRow = CopyRow_NEON;
  }
532 533
#endif
#if defined(HAS_COPYROW_X86)
534 535
  if (IS_ALIGNED(width, 4)) {
    CopyRow = CopyRow_X86;
536
  }
537
#endif
538 539 540 541 542
#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;
543 544
  }
#endif
545 546 547
#if defined(HAS_COPYROW_ERMS)
  if (TestCpuFlag(kCpuHasERMS)) {
    CopyRow = CopyRow_ERMS;
548 549
  }
#endif
550 551 552 553 554
#if defined(HAS_COPYROW_MIPS)
  if (TestCpuFlag(kCpuHasMIPS)) {
    CopyRow = CopyRow_MIPS;
  }
#endif
555

556 557 558 559 560
  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)
561 562 563
  if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
564 565 566 567 568
    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;
569 570 571
        if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
          YUY2ToYRow = YUY2ToYRow_SSE2;
        }
572 573 574
      }
    }
  }
575 576 577 578 579 580 581 582 583 584 585 586
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    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)
587 588 589 590
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
    if (width >= 16) {
      YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
591
    }
592
    if (IS_ALIGNED(width, 16)) {
593
      YUY2ToYRow = YUY2ToYRow_NEON;
594
      YUY2ToUV422Row = YUY2ToUV422Row_NEON;
595
    }
596
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
597 598
#endif

599
  for (int y = 0; y < height - 1; y += 2) {
600
    CopyRow(src_y, dst_y, width);
601
    src_y += src_stride_y;
602
    dst_y += dst_stride_y;
603

604 605 606
    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
    src_yuy2 += src_stride_yuy2;
607 608 609
    dst_y += dst_stride_y;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
610 611 612 613
  }
  if (height & 1) {
    CopyRow(src_y, dst_y, width);
    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
614
  }
615
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
616 617
}

618
// Convert YUY2 to I420.
619
LIBYUV_API
620
int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
621 622 623 624
               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) {
625
  // Negative height means invert the image.
626 627
  if (height < 0) {
    height = -height;
628 629 630 631
    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,
632
                      uint8* dst_u, uint8* dst_v, int pix);
633
  void (*YUY2ToYRow)(const uint8* src_yuy2,
634
                     uint8* dst_y, int pix);
635 636 637
  YUY2ToYRow = YUY2ToYRow_C;
  YUY2ToUVRow = YUY2ToUVRow_C;
#if defined(HAS_YUY2TOYROW_SSE2)
638 639 640
  if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    YUY2ToUVRow = YUY2ToUVRow_Any_SSE2;
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
641
    if (IS_ALIGNED(width, 16)) {
642 643 644 645 646 647 648 649
      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;
        }
      }
650
    }
651
  }
652 653 654 655 656 657 658 659 660 661 662 663
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    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)
664 665 666 667
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
    if (width >= 16) {
      YUY2ToUVRow = YUY2ToUVRow_Any_NEON;
668
    }
669
    if (IS_ALIGNED(width, 16)) {
670
      YUY2ToYRow = YUY2ToYRow_NEON;
671
      YUY2ToUVRow = YUY2ToUVRow_NEON;
672 673
    }
  }
674
#endif
675

676 677 678 679 680 681 682 683
  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;
684
  }
685
  if (height & 1) {
686 687
    YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
688 689 690 691 692
  }
  return 0;
}

// Convert UYVY to I420.
693
LIBYUV_API
694 695 696 697 698 699 700 701 702 703 704 705
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,
706
                      uint8* dst_u, uint8* dst_v, int pix);
707
  void (*UYVYToYRow)(const uint8* src_uyvy,
708
                     uint8* dst_y, int pix);
709 710 711
  UYVYToYRow = UYVYToYRow_C;
  UYVYToUVRow = UYVYToUVRow_C;
#if defined(HAS_UYVYTOYROW_SSE2)
712 713 714
  if (TestCpuFlag(kCpuHasSSE2) && width >= 16) {
    UYVYToUVRow = UYVYToUVRow_Any_SSE2;
    UYVYToYRow = UYVYToYRow_Any_SSE2;
715
    if (IS_ALIGNED(width, 16)) {
716 717 718 719 720 721 722 723
      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;
        }
      }
724
    }
725
  }
726 727 728 729 730 731 732 733 734 735 736 737
#endif
#if defined(HAS_UYVYTOYROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    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)
738 739 740 741
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    UYVYToYRow = UYVYToYRow_Any_NEON;
    if (width >= 16) {
      UYVYToUVRow = UYVYToUVRow_Any_NEON;
742
    }
743
    if (IS_ALIGNED(width, 16)) {
744
      UYVYToYRow = UYVYToYRow_NEON;
745
      UYVYToUVRow = UYVYToUVRow_NEON;
746 747
    }
  }
748
#endif
749

750 751 752 753 754
  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;
755 756 757
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
758
  }
759
  if (height & 1) {
760 761 762 763 764 765
    UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width);
    UYVYToYRow(src_uyvy, dst_y, width);
  }
  return 0;
}

766
// Convert ARGB to I420.
767
LIBYUV_API
768
int ARGBToI420(const uint8* src_argb, int src_stride_argb,
769 770 771 772
               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) {
773 774 775 776 777 778
  if (!src_argb ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
779 780
  if (height < 0) {
    height = -height;
781 782
    src_argb = src_argb + (height - 1) * src_stride_argb;
    src_stride_argb = -src_stride_argb;
783 784
  }
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
785 786 787
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int pix) =
      ARGBToYRow_C;
788
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
789 790 791
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
792
    if (IS_ALIGNED(width, 16)) {
793 794 795 796 797 798 799 800
      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;
        }
      }
801
    }
802
  }
803
#endif
804
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
805 806 807 808
  if (TestCpuFlag(kCpuHasAVX2) && width >= 32) {
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
809 810
      ARGBToUVRow = ARGBToUVRow_AVX2;
      ARGBToYRow = ARGBToYRow_AVX2;
811 812 813 814
    }
  }
#endif
#if defined(HAS_ARGBTOYROW_NEON)
815 816
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    ARGBToYRow = ARGBToYRow_Any_NEON;
817 818
    if (IS_ALIGNED(width, 8)) {
      ARGBToYRow = ARGBToYRow_NEON;
819 820 821
    }
    if (width >= 16) {
      ARGBToUVRow = ARGBToUVRow_Any_NEON;
fbarchard@google.com's avatar
fbarchard@google.com committed
822 823 824
      if (IS_ALIGNED(width, 16)) {
        ARGBToUVRow = ARGBToUVRow_NEON;
      }
825 826
    }
  }
827
#endif
828 829 830 831 832 833 834 835 836

  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;
837
  }
838 839 840 841 842 843 844
  if (height & 1) {
    ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
    ARGBToYRow(src_argb, dst_y, width);
  }
  return 0;
}

845
// Convert BGRA to I420.
846
LIBYUV_API
847 848 849 850 851
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) {
852 853 854 855 856 857
  if (!src_bgra ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
858 859 860 861 862 863
  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,
864 865 866
                      uint8* dst_u, uint8* dst_v, int width) = BGRAToUVRow_C;
  void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int pix) =
      BGRAToYRow_C;
867
#if defined(HAS_BGRATOYROW_SSSE3)
868 869 870
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    BGRAToUVRow = BGRAToUVRow_Any_SSSE3;
    BGRAToYRow = BGRAToYRow_Any_SSSE3;
871
    if (IS_ALIGNED(width, 16)) {
872 873 874 875 876 877 878 879
      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;
        }
      }
880
    }
881
  }
882 883 884 885 886 887
#elif defined(HAS_BGRATOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    BGRAToYRow = BGRAToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      BGRAToYRow = BGRAToYRow_NEON;
    }
888 889 890 891 892 893
    if (width >= 16) {
      BGRAToUVRow = BGRAToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        BGRAToUVRow = BGRAToUVRow_NEON;
      }
    }
894
  }
895
#endif
896

897 898 899 900 901
  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;
902 903 904 905 906
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
907 908
    BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width);
    BGRAToYRow(src_bgra, dst_y, width);
909 910
  }
  return 0;
911 912
}

913
// Convert ABGR to I420.
914
LIBYUV_API
915
int ABGRToI420(const uint8* src_abgr, int src_stride_abgr,
916 917 918 919
               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) {
920 921 922 923 924 925
  if (!src_abgr ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
926 927
  if (height < 0) {
    height = -height;
928 929
    src_abgr = src_abgr + (height - 1) * src_stride_abgr;
    src_stride_abgr = -src_stride_abgr;
930
  }
931
  void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr,
932 933 934
                      uint8* dst_u, uint8* dst_v, int width) = ABGRToUVRow_C;
  void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int pix) =
      ABGRToYRow_C;
935
#if defined(HAS_ABGRTOYROW_SSSE3)
936 937 938
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
    ABGRToYRow = ABGRToYRow_Any_SSSE3;
939
    if (IS_ALIGNED(width, 16)) {
940 941 942 943 944 945 946 947
      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;
        }
      }
948
    }
949
  }
950 951 952 953 954 955
#elif defined(HAS_ABGRTOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    ABGRToYRow = ABGRToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ABGRToYRow = ABGRToYRow_NEON;
    }
956 957 958 959 960 961
    if (width >= 16) {
      ABGRToUVRow = ABGRToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        ABGRToUVRow = ABGRToUVRow_NEON;
      }
    }
962
  }
963
#endif
964

965 966 967 968 969
  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;
970 971 972 973 974
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
975 976
    ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
    ABGRToYRow(src_abgr, dst_y, width);
977 978
  }
  return 0;
979 980
}

981
// Convert RGBA to I420.
982
LIBYUV_API
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
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,
1000 1001 1002
                      uint8* dst_u, uint8* dst_v, int width) = RGBAToUVRow_C;
  void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int pix) =
      RGBAToYRow_C;
1003
#if defined(HAS_RGBATOYROW_SSSE3)
1004 1005 1006
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    RGBAToUVRow = RGBAToUVRow_Any_SSSE3;
    RGBAToYRow = RGBAToYRow_Any_SSSE3;
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
    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;
        }
      }
    }
  }
1018 1019 1020 1021 1022 1023
#elif defined(HAS_RGBATOYROW_NEON)
  if (TestCpuFlag(kCpuHasNEON) && width >= 8) {
    RGBAToYRow = RGBAToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGBAToYRow = RGBAToYRow_NEON;
    }
1024 1025 1026 1027 1028 1029
    if (width >= 16) {
      RGBAToUVRow = RGBAToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        RGBAToUVRow = RGBAToUVRow_NEON;
      }
    }
1030
  }
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
#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;
}

1049
// Convert RGB24 to I420.
1050
LIBYUV_API SAFEBUFFERS
1051
int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24,
1052 1053 1054 1055
                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) {
1056 1057 1058
  if (!src_rgb24 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1059 1060
    return -1;
  }
1061
  // Negative height means invert the image.
1062 1063
  if (height < 0) {
    height = -height;
1064 1065
    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
    src_stride_rgb24 = -src_stride_rgb24;
1066
  }
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085

#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
1086
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1087 1088
  void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      RGB24ToARGBRow_C;
1089
#if defined(HAS_RGB24TOARGBROW_SSSE3)
1090 1091 1092 1093 1094 1095
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    RGB24ToARGBRow = RGB24ToARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
    }
  }
1096 1097
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
                      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) {
1111
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1112 1113
    if (IS_ALIGNED(width, 16)) {
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1114 1115 1116
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1117
    }
1118
  }
1119 1120
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_RGB24TOYROW_NEON
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
1121

1122
  for (int y = 0; y < height - 1; y += 2) {
1123
#if defined(HAS_RGB24TOYROW_NEON)
1124
    RGB24ToUVRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width);
1125 1126 1127
    RGB24ToYRow(src_rgb24, dst_y, width);
    RGB24ToYRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width);
#else
1128 1129 1130
    RGB24ToARGBRow(src_rgb24, row, width);
    RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kMaxStride, width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1131 1132
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1133
#endif
1134
    src_rgb24 += src_stride_rgb24 * 2;
1135 1136 1137 1138 1139
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1140
#if defined(HAS_RGB24TOYROW_NEON)
1141
    RGB24ToUVRow(src_rgb24, 0, dst_u, dst_v, width);
1142 1143
    RGB24ToYRow(src_rgb24, dst_y, width);
#else
1144 1145
    RGB24ToARGBRow(src_rgb24, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1146
    ARGBToYRow(row, dst_y, width);
1147
#endif
1148 1149
  }
  return 0;
1150 1151
}

1152
// Convert RAW to I420.
1153
LIBYUV_API SAFEBUFFERS
1154
int RAWToI420(const uint8* src_raw, int src_stride_raw,
1155 1156 1157 1158
              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) {
1159 1160 1161
  if (!src_raw || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1162 1163
    return -1;
  }
1164
  // Negative height means invert the image.
1165 1166
  if (height < 0) {
    height = -height;
1167 1168
    src_raw = src_raw + (height - 1) * src_stride_raw;
    src_stride_raw = -src_stride_raw;
1169
  }
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188

#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
1189
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1190 1191
  void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      RAWToARGBRow_C;
1192
#if defined(HAS_RAWTOARGBROW_SSSE3)
1193 1194 1195 1196 1197 1198
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    RAWToARGBRow = RAWToARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      RAWToARGBRow = RAWToARGBRow_SSSE3;
    }
  }
1199 1200
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
                      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) {
1214
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1215 1216
    if (IS_ALIGNED(width, 16)) {
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1217 1218 1219
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1220
    }
1221
  }
1222 1223
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_RAWTOYROW_NEON
1224

1225
  for (int y = 0; y < height - 1; y += 2) {
1226
#if defined(HAS_RAWTOYROW_NEON)
1227
    RAWToUVRow(src_raw, src_stride_raw, dst_u, dst_v, width);
1228 1229 1230
    RAWToYRow(src_raw, dst_y, width);
    RAWToYRow(src_raw + src_stride_raw, dst_y + dst_stride_y, width);
#else
1231 1232 1233
    RAWToARGBRow(src_raw, row, width);
    RAWToARGBRow(src_raw + src_stride_raw, row + kMaxStride, width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1234 1235
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1236
#endif
1237
    src_raw += src_stride_raw * 2;
1238 1239 1240 1241 1242
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1243
#if defined(HAS_RAWTOYROW_NEON)
1244
    RAWToUVRow(src_raw, 0, dst_u, dst_v, width);
1245 1246
    RAWToYRow(src_raw, dst_y, width);
#else
1247 1248
    RAWToARGBRow(src_raw, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1249
    ARGBToYRow(row, dst_y, width);
1250
#endif
1251 1252
  }
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
1253 1254
}

1255
// Convert RGB565 to I420.
1256
LIBYUV_API SAFEBUFFERS
1257
int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565,
1258 1259 1260 1261 1262 1263 1264
                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) {
1265 1266
    return -1;
  }
1267
  // Negative height means invert the image.
1268 1269
  if (height < 0) {
    height = -height;
1270 1271
    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
    src_stride_rgb565 = -src_stride_rgb565;
1272
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291

#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
1292
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1293 1294
  void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      RGB565ToARGBRow_C;
1295
#if defined(HAS_RGB565TOARGBROW_SSE2)
1296 1297 1298 1299 1300 1301
  if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
    RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
    }
  }
1302 1303
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316
                      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) {
1317
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1318 1319
    if (IS_ALIGNED(width, 16)) {
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1320 1321 1322
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1323
    }
1324
  }
1325 1326
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_RGB565TOYROW_NEON
1327

1328
  for (int y = 0; y < height - 1; y += 2) {
1329
#if defined(HAS_RGB565TOYROW_NEON)
fbarchard@google.com's avatar
fbarchard@google.com committed
1330
    RGB565ToUVRow(src_rgb565, src_stride_rgb565, dst_u, dst_v, width);
1331 1332 1333
    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
1334 1335 1336
    RGB565ToARGBRow(src_rgb565, row, width);
    RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kMaxStride, width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1337 1338
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1339
#endif
1340
    src_rgb565 += src_stride_rgb565 * 2;
1341 1342 1343 1344 1345
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1346
#if defined(HAS_RGB565TOYROW_NEON)
fbarchard@google.com's avatar
fbarchard@google.com committed
1347
    RGB565ToUVRow(src_rgb565, 0, dst_u, dst_v, width);
1348 1349
    RGB565ToYRow(src_rgb565, dst_y, width);
#else
fbarchard@google.com's avatar
fbarchard@google.com committed
1350 1351
    RGB565ToARGBRow(src_rgb565, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1352
    ARGBToYRow(row, dst_y, width);
1353
#endif
1354 1355 1356 1357
  }
  return 0;
}

1358
// Convert ARGB1555 to I420.
1359
LIBYUV_API SAFEBUFFERS
1360
int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555,
1361 1362 1363 1364
                   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) {
1365 1366 1367
  if (!src_argb1555 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1368 1369
    return -1;
  }
1370
  // Negative height means invert the image.
1371 1372
  if (height < 0) {
    height = -height;
1373 1374
    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
    src_stride_argb1555 = -src_stride_argb1555;
1375
  }
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394

#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
1395
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1396 1397
  void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      ARGB1555ToARGBRow_C;
1398
#if defined(HAS_ARGB1555TOARGBROW_SSE2)
1399 1400 1401 1402 1403 1404
  if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
    }
  }
1405 1406
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1407 1408
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
1409 1410
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1411
    if (IS_ALIGNED(width, 16)) {
1412
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1413 1414 1415 1416 1417 1418 1419 1420 1421
    }
  }
#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)) {
1422
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1423 1424 1425
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1426
    }
1427
  }
1428 1429
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_ARGB1555TOYROW_NEON
1430

1431
  for (int y = 0; y < height - 1; y += 2) {
1432
#if defined(HAS_ARGB1555TOYROW_NEON)
1433
    ARGB1555ToUVRow(src_argb1555, src_stride_argb1555, dst_u, dst_v, width);
1434
    ARGB1555ToYRow(src_argb1555, dst_y, width);
1435 1436
    ARGB1555ToYRow(src_argb1555 + src_stride_argb1555, dst_y + dst_stride_y,
                   width);
1437
#else
1438
    ARGB1555ToARGBRow(src_argb1555, row, width);
1439 1440
    ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555, row + kMaxStride,
                      width);
1441
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1442 1443
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1444
#endif
1445
    src_argb1555 += src_stride_argb1555 * 2;
1446 1447 1448 1449 1450
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1451
#if defined(HAS_ARGB1555TOYROW_NEON)
1452
    ARGB1555ToUVRow(src_argb1555, 0, dst_u, dst_v, width);
1453 1454
    ARGB1555ToYRow(src_argb1555, dst_y, width);
#else
1455 1456
    ARGB1555ToARGBRow(src_argb1555, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1457
    ARGBToYRow(row, dst_y, width);
1458
#endif
1459 1460 1461 1462
  }
  return 0;
}

1463
// Convert ARGB4444 to I420.
1464
LIBYUV_API SAFEBUFFERS
1465
int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444,
1466 1467 1468 1469
                   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) {
1470 1471 1472
  if (!src_argb4444 || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0 ||
      width * 4 > kMaxStride) {
1473 1474
    return -1;
  }
1475
  // Negative height means invert the image.
1476 1477
  if (height < 0) {
    height = -height;
1478 1479
    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
    src_stride_argb4444 = -src_stride_argb4444;
1480
  }
1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499

#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
1500
  SIMD_ALIGNED(uint8 row[kMaxStride * 2]);
1501 1502
  void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int pix) =
      ARGB4444ToARGBRow_C;
1503
#if defined(HAS_ARGB4444TOARGBROW_SSE2)
1504 1505 1506 1507 1508 1509
  if (TestCpuFlag(kCpuHasSSE2) && width >= 8) {
    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
    }
  }
1510 1511
#endif
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1512 1513
                      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
#if defined(HAS_ARGBTOUVROW_SSSE3)
1514 1515
  if (TestCpuFlag(kCpuHasSSSE3) && width >= 16) {
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1516
    if (IS_ALIGNED(width, 16)) {
1517
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1518 1519 1520 1521 1522 1523 1524 1525 1526
    }
  }
#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)) {
1527
      ARGBToYRow = ARGBToYRow_Unaligned_SSSE3;
1528 1529 1530
      if (IS_ALIGNED(dst_y, 16) && IS_ALIGNED(dst_stride_y, 16)) {
        ARGBToYRow = ARGBToYRow_SSSE3;
      }
1531
    }
1532
  }
1533 1534
#endif  // HAS_ARGBTOUVROW_SSSE3
#endif  // HAS_ARGB4444TOYROW_NEON
1535

1536
  for (int y = 0; y < height - 1; y += 2) {
1537
#if defined(HAS_ARGB4444TOYROW_NEON)
1538
    ARGB4444ToUVRow(src_argb4444, src_stride_argb4444, dst_u, dst_v, width);
1539
    ARGB4444ToYRow(src_argb4444, dst_y, width);
1540 1541
    ARGB4444ToYRow(src_argb4444 + src_stride_argb4444, dst_y + dst_stride_y,
                   width);
1542
#else
1543 1544 1545 1546
    ARGB4444ToARGBRow(src_argb4444, row, width);
    ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444, row + kMaxStride,
                      width);
    ARGBToUVRow(row, kMaxStride, dst_u, dst_v, width);
1547 1548
    ARGBToYRow(row, dst_y, width);
    ARGBToYRow(row + kMaxStride, dst_y + dst_stride_y, width);
1549
#endif
1550
    src_argb4444 += src_stride_argb4444 * 2;
1551 1552 1553 1554 1555
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
1556
#if defined(HAS_ARGB4444TOYROW_NEON)
1557
    ARGB4444ToUVRow(src_argb4444, 0, dst_u, dst_v, width);
1558 1559
    ARGB4444ToYRow(src_argb4444, dst_y, width);
#else
1560 1561
    ARGB4444ToARGBRow(src_argb4444, row, width);
    ARGBToUVRow(row, 0, dst_u, dst_v, width);
1562
    ARGBToYRow(row, dst_y, width);
1563
#endif
1564 1565 1566 1567
  }
  return 0;
}

1568 1569 1570 1571
#ifdef __cplusplus
}  // extern "C"
}  // namespace libyuv
#endif