convert.cc 43.7 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
static __inline int Abs(int v) {
  return v >= 0 ? v : -v;
}

// Any I4xx To I420 format with mirroring.
static int I4xxToI420(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 src_y_width, int src_y_height,
                      int src_uv_width, int src_uv_height) {
  const int dst_y_width = Abs(src_y_width);
  const int dst_y_height = Abs(src_y_height);
  const int dst_uv_width = SUBSAMPLE(dst_y_width, 1, 1);
  const int dst_uv_height = SUBSAMPLE(dst_y_height, 1, 1);
43 44 45 46
  if (src_y_width == 0 || src_y_height == 0 ||
      src_uv_width == 0 || src_uv_height == 0) {
    return -1;
  }
47 48 49 50 51 52 53 54 55 56 57 58
  ScalePlane(src_y, src_stride_y, src_y_width, src_y_height,
             dst_y, dst_stride_y, dst_y_width, dst_y_height,
             kFilterBilinear);
  ScalePlane(src_u, src_stride_u, src_uv_width, src_uv_height,
             dst_u, dst_stride_u, dst_uv_width, dst_uv_height,
             kFilterBilinear);
  ScalePlane(src_v, src_stride_v, src_uv_width, src_uv_height,
             dst_v, dst_stride_v, dst_uv_width, dst_uv_height,
             kFilterBilinear);
  return 0;
}

59
// Copy I420 with optional flipping
60 61
// TODO(fbarchard): Use Scale plane which supports mirroring, but ensure
// is does row coalescing.
62
LIBYUV_API
63 64 65 66 67 68 69
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) {
70 71
  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;
72 73 74 75
  if (!src_y || !src_u || !src_v ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
76
  }
77 78 79
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
80
    halfheight = (height + 1) >> 1;
81 82 83 84 85 86 87 88 89 90 91
    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;
  }

  if (dst_y) {
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  }
92
  // Copy UV planes.
93 94 95
  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;
96
}
97

98 99
// 422 chroma is 1/2 width, 1x height
// 420 chroma is 1/2 width, 1/2 height
100
LIBYUV_API
101
int I422ToI420(const uint8* src_y, int src_stride_y,
102 103
               const uint8* src_u, int src_stride_u,
               const uint8* src_v, int src_stride_v,
104 105 106
               uint8* dst_y, int dst_stride_y,
               uint8* dst_u, int dst_stride_u,
               uint8* dst_v, int dst_stride_v,
107
               int width, int height) {
108 109 110 111 112 113 114 115 116
  const int src_uv_width = SUBSAMPLE(width, 1, 1);
  return I4xxToI420(src_y, src_stride_y,
                    src_u, src_stride_u,
                    src_v, src_stride_v,
                    dst_y, dst_stride_y,
                    dst_u, dst_stride_u,
                    dst_v, dst_stride_v,
                    width, height,
                    src_uv_width, height);
117
}
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
118

119 120
// 444 chroma is 1x width, 1x height
// 420 chroma is 1/2 width, 1/2 height
121
LIBYUV_API
122
int I444ToI420(const uint8* src_y, int src_stride_y,
123 124
               const uint8* src_u, int src_stride_u,
               const uint8* src_v, int src_stride_v,
125 126 127
               uint8* dst_y, int dst_stride_y,
               uint8* dst_u, int dst_stride_u,
               uint8* dst_v, int dst_stride_v,
128
               int width, int height) {
129 130 131 132 133 134 135 136
  return I4xxToI420(src_y, src_stride_y,
                    src_u, src_stride_u,
                    src_v, src_stride_v,
                    dst_y, dst_stride_y,
                    dst_u, dst_stride_u,
                    dst_v, dst_stride_v,
                    width, height,
                    width, height);
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
137 138
}

139 140
// 411 chroma is 1/4 width, 1x height
// 420 chroma is 1/2 width, 1/2 height
141
LIBYUV_API
142 143 144 145 146 147 148
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) {
149 150 151 152 153 154 155 156 157
  const int src_uv_width = SUBSAMPLE(width, 3, 2);
  return I4xxToI420(src_y, src_stride_y,
                    src_u, src_stride_u,
                    src_v, src_stride_v,
                    dst_y, dst_stride_y,
                    dst_u, dst_stride_u,
                    dst_v, dst_stride_v,
                    width, height,
                    src_uv_width, height);
158 159
}

160
// I400 is greyscale typically used in MJPG
161
LIBYUV_API
162 163 164 165
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,
166
               int width, int height) {
167 168
  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;
169 170 171 172
  if (!src_y || !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
173 174 175
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
176
    halfheight = (height + 1) >> 1;
177 178 179 180 181 182
    src_y = src_y + (height - 1) * src_stride_y;
    src_stride_y = -src_stride_y;
  }
  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);
183 184 185
  return 0;
}

186
static void CopyPlane2(const uint8* src, int src_stride_0, int src_stride_1,
187
                       uint8* dst, int dst_stride,
188
                       int width, int height) {
189
  int y;
190 191
  void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
#if defined(HAS_COPYROW_SSE2)
192 193
  if (TestCpuFlag(kCpuHasSSE2)) {
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
194
  }
195
#endif
196
#if defined(HAS_COPYROW_AVX)
197 198
  if (TestCpuFlag(kCpuHasAVX)) {
    CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
199 200
  }
#endif
201 202 203
#if defined(HAS_COPYROW_ERMS)
  if (TestCpuFlag(kCpuHasERMS)) {
    CopyRow = CopyRow_ERMS;
204 205 206
  }
#endif
#if defined(HAS_COPYROW_NEON)
207 208
  if (TestCpuFlag(kCpuHasNEON)) {
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
209 210
  }
#endif
211 212 213 214 215
#if defined(HAS_COPYROW_MIPS)
  if (TestCpuFlag(kCpuHasMIPS)) {
    CopyRow = CopyRow_MIPS;
  }
#endif
216

217
  // Copy plane
218
  for (y = 0; y < height - 1; y += 2) {
219
    CopyRow(src, dst, width);
220
    CopyRow(src + src_stride_0, dst + dst_stride, width);
221
    src += src_stride_0 + src_stride_1;
222
    dst += dst_stride * 2;
223 224 225
  }
  if (height & 1) {
    CopyRow(src, dst, width);
226
  }
227 228 229 230 231 232
}

// 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:
233
// M420 is row biplanar 420: 2 rows of Y and 1 row of UV.
234
// Chroma is half width / half height. (420)
235
// src_stride_m420 is row planar. Normally this will be the width in pixels.
236 237 238 239 240 241 242 243 244
//   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) {
245 246 247
  int y;
  int halfwidth = (width + 1) >> 1;
  int halfheight = (height + 1) >> 1;
248 249
  void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
                     int width) = SplitUVRow_C;
250 251 252 253 254
  if (!src_y || !src_uv ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
255 256 257
  // Negative height means invert the image.
  if (height < 0) {
    height = -height;
258
    halfheight = (height + 1) >> 1;
259 260 261 262 263 264 265
    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;
  }
266
  // Coalesce rows.
267 268 269
  if (src_stride_y0 == width &&
      src_stride_y1 == width &&
      dst_stride_y == width) {
270
    width *= height;
271
    height = 1;
272
    src_stride_y0 = src_stride_y1 = dst_stride_y = 0;
273
  }
274 275
  // Coalesce rows.
  if (src_stride_uv == halfwidth * 2 &&
276 277
      dst_stride_u == halfwidth &&
      dst_stride_v == halfwidth) {
278
    halfwidth *= halfheight;
279
    halfheight = 1;
280
    src_stride_uv = dst_stride_u = dst_stride_v = 0;
281
  }
282
#if defined(HAS_SPLITUVROW_SSE2)
283
  if (TestCpuFlag(kCpuHasSSE2)) {
284
    SplitUVRow = SplitUVRow_Any_SSE2;
285
    if (IS_ALIGNED(halfwidth, 16)) {
286
      SplitUVRow = SplitUVRow_SSE2;
287
    }
fbarchard@google.com's avatar
fbarchard@google.com committed
288
  }
289
#endif
290
#if defined(HAS_SPLITUVROW_AVX2)
291
  if (TestCpuFlag(kCpuHasAVX2)) {
292
    SplitUVRow = SplitUVRow_Any_AVX2;
293
    if (IS_ALIGNED(halfwidth, 32)) {
294
      SplitUVRow = SplitUVRow_AVX2;
295
    }
296
  }
297
#endif
298
#if defined(HAS_SPLITUVROW_NEON)
299
  if (TestCpuFlag(kCpuHasNEON)) {
300
    SplitUVRow = SplitUVRow_Any_NEON;
301
    if (IS_ALIGNED(halfwidth, 16)) {
302
      SplitUVRow = SplitUVRow_NEON;
303 304 305
    }
  }
#endif
306 307
#if defined(HAS_SPLITUVROW_DSPR2)
  if (TestCpuFlag(kCpuHasDSPR2) &&
308 309 310
      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)) {
311
    SplitUVRow = SplitUVRow_Any_DSPR2;
312
    if (IS_ALIGNED(halfwidth, 16)) {
313
      SplitUVRow = SplitUVRow_DSPR2;
314
    }
315
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
316
#endif
317

318
  if (dst_y) {
319 320 321 322 323 324
    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);
    }
325
  }
326

327
  for (y = 0; y < halfheight; ++y) {
328
    // Copy a row of UV.
329
    SplitUVRow(src_uv, dst_u, dst_v, halfwidth);
330 331 332
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
    src_uv += src_stride_uv;
333 334 335 336
  }
  return 0;
}

337
// Convert NV12 to I420.
338
LIBYUV_API
339 340 341 342 343
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,
344
               int width, int height) {
345 346 347 348 349 350 351 352
  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);
}

353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
// 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);
}

369
// Convert M420 to I420.
370
LIBYUV_API
371 372 373 374 375 376 377 378 379 380 381 382 383
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);
}

384
// Convert YUY2 to I420.
385
LIBYUV_API
386
int YUY2ToI420(const uint8* src_yuy2, int src_stride_yuy2,
387 388 389 390
               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) {
391 392
  int y;
  void (*YUY2ToUVRow)(const uint8* src_yuy2, int src_stride_yuy2,
393
      uint8* dst_u, uint8* dst_v, int width) = YUY2ToUVRow_C;
394
  void (*YUY2ToYRow)(const uint8* src_yuy2,
395
      uint8* dst_y, int width) = YUY2ToYRow_C;
396
  // Negative height means invert the image.
397 398
  if (height < 0) {
    height = -height;
399 400 401 402
    src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
    src_stride_yuy2 = -src_stride_yuy2;
  }
#if defined(HAS_YUY2TOYROW_SSE2)
403
  if (TestCpuFlag(kCpuHasSSE2)) {
404 405
    YUY2ToUVRow = YUY2ToUVRow_Any_SSE2;
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
406
    if (IS_ALIGNED(width, 16)) {
407 408
      YUY2ToUVRow = YUY2ToUVRow_SSE2;
      YUY2ToYRow = YUY2ToYRow_SSE2;
409
    }
410
  }
411 412
#endif
#if defined(HAS_YUY2TOYROW_AVX2)
413
  if (TestCpuFlag(kCpuHasAVX2)) {
414 415 416 417 418 419 420 421 422
    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)
423
  if (TestCpuFlag(kCpuHasNEON)) {
424
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
425
    YUY2ToUVRow = YUY2ToUVRow_Any_NEON;
426
    if (IS_ALIGNED(width, 16)) {
427
      YUY2ToYRow = YUY2ToYRow_NEON;
428
      YUY2ToUVRow = YUY2ToUVRow_NEON;
429 430
    }
  }
431
#endif
432

433
  for (y = 0; y < height - 1; y += 2) {
434 435 436 437 438 439 440
    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;
441
  }
442
  if (height & 1) {
443 444
    YUY2ToUVRow(src_yuy2, 0, dst_u, dst_v, width);
    YUY2ToYRow(src_yuy2, dst_y, width);
445 446 447 448 449
  }
  return 0;
}

// Convert UYVY to I420.
450
LIBYUV_API
451 452 453 454 455
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) {
456 457
  int y;
  void (*UYVYToUVRow)(const uint8* src_uyvy, int src_stride_uyvy,
458
      uint8* dst_u, uint8* dst_v, int width) = UYVYToUVRow_C;
459
  void (*UYVYToYRow)(const uint8* src_uyvy,
460
      uint8* dst_y, int width) = UYVYToYRow_C;
461 462 463 464 465 466 467
  // 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;
  }
#if defined(HAS_UYVYTOYROW_SSE2)
468
  if (TestCpuFlag(kCpuHasSSE2)) {
469 470
    UYVYToUVRow = UYVYToUVRow_Any_SSE2;
    UYVYToYRow = UYVYToYRow_Any_SSE2;
471
    if (IS_ALIGNED(width, 16)) {
472 473
      UYVYToUVRow = UYVYToUVRow_SSE2;
      UYVYToYRow = UYVYToYRow_SSE2;
474
    }
475
  }
476 477
#endif
#if defined(HAS_UYVYTOYROW_AVX2)
478
  if (TestCpuFlag(kCpuHasAVX2)) {
479 480 481 482 483 484 485 486 487
    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)
488
  if (TestCpuFlag(kCpuHasNEON)) {
489
    UYVYToYRow = UYVYToYRow_Any_NEON;
490
    UYVYToUVRow = UYVYToUVRow_Any_NEON;
491
    if (IS_ALIGNED(width, 16)) {
492
      UYVYToYRow = UYVYToYRow_NEON;
493
      UYVYToUVRow = UYVYToUVRow_NEON;
494 495
    }
  }
496
#endif
497

498
  for (y = 0; y < height - 1; y += 2) {
499 500 501 502
    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;
503 504 505
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
506
  }
507
  if (height & 1) {
508 509 510 511 512 513
    UYVYToUVRow(src_uyvy, 0, dst_u, dst_v, width);
    UYVYToYRow(src_uyvy, dst_y, width);
  }
  return 0;
}

514
// Convert ARGB to I420.
515
LIBYUV_API
516
int ARGBToI420(const uint8* src_argb, int src_stride_argb,
517 518 519 520
               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) {
521 522
  int y;
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
523
      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
524
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
525
      ARGBToYRow_C;
526 527 528 529 530 531
  if (!src_argb ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
532 533
  if (height < 0) {
    height = -height;
534 535
    src_argb = src_argb + (height - 1) * src_stride_argb;
    src_stride_argb = -src_stride_argb;
536
  }
537
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
538
  if (TestCpuFlag(kCpuHasSSSE3)) {
539 540
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
541
    if (IS_ALIGNED(width, 16)) {
542 543
      ARGBToUVRow = ARGBToUVRow_SSSE3;
      ARGBToYRow = ARGBToYRow_SSSE3;
544
    }
545
  }
546
#endif
547
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
548
  if (TestCpuFlag(kCpuHasAVX2)) {
549
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
550 551
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
552
      ARGBToUVRow = ARGBToUVRow_AVX2;
553
      ARGBToYRow = ARGBToYRow_AVX2;
554 555 556 557
    }
  }
#endif
#if defined(HAS_ARGBTOYROW_NEON)
558
  if (TestCpuFlag(kCpuHasNEON)) {
559
    ARGBToYRow = ARGBToYRow_Any_NEON;
560 561
    if (IS_ALIGNED(width, 8)) {
      ARGBToYRow = ARGBToYRow_NEON;
562
    }
563 564 565
  }
#endif
#if defined(HAS_ARGBTOUVROW_NEON)
566
  if (TestCpuFlag(kCpuHasNEON)) {
567 568 569
    ARGBToUVRow = ARGBToUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      ARGBToUVRow = ARGBToUVRow_NEON;
570 571
    }
  }
572
#endif
573

574
  for (y = 0; y < height - 1; y += 2) {
575 576 577 578 579 580 581
    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;
582
  }
583 584 585 586 587 588 589
  if (height & 1) {
    ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
    ARGBToYRow(src_argb, dst_y, width);
  }
  return 0;
}

590
// Convert BGRA to I420.
591
LIBYUV_API
592 593 594 595 596
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) {
597 598
  int y;
  void (*BGRAToUVRow)(const uint8* src_bgra0, int src_stride_bgra,
599
      uint8* dst_u, uint8* dst_v, int width) = BGRAToUVRow_C;
600
  void (*BGRAToYRow)(const uint8* src_bgra, uint8* dst_y, int width) =
601
      BGRAToYRow_C;
602 603 604 605 606 607
  if (!src_bgra ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
608 609 610 611 612
  if (height < 0) {
    height = -height;
    src_bgra = src_bgra + (height - 1) * src_stride_bgra;
    src_stride_bgra = -src_stride_bgra;
  }
613
#if defined(HAS_BGRATOYROW_SSSE3) && defined(HAS_BGRATOUVROW_SSSE3)
614
  if (TestCpuFlag(kCpuHasSSSE3)) {
615 616
    BGRAToUVRow = BGRAToUVRow_Any_SSSE3;
    BGRAToYRow = BGRAToYRow_Any_SSSE3;
617
    if (IS_ALIGNED(width, 16)) {
618 619
      BGRAToUVRow = BGRAToUVRow_SSSE3;
      BGRAToYRow = BGRAToYRow_SSSE3;
620
    }
621
  }
622 623
#endif
#if defined(HAS_BGRATOYROW_NEON)
624
  if (TestCpuFlag(kCpuHasNEON)) {
625 626 627 628
    BGRAToYRow = BGRAToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      BGRAToYRow = BGRAToYRow_NEON;
    }
629 630 631
  }
#endif
#if defined(HAS_BGRATOUVROW_NEON)
632
    if (TestCpuFlag(kCpuHasNEON)) {
633 634 635 636 637
      BGRAToUVRow = BGRAToUVRow_Any_NEON;
      if (IS_ALIGNED(width, 16)) {
        BGRAToUVRow = BGRAToUVRow_NEON;
      }
    }
638
#endif
639

640
  for (y = 0; y < height - 1; y += 2) {
641 642 643 644
    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;
645 646 647 648 649
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
650 651
    BGRAToUVRow(src_bgra, 0, dst_u, dst_v, width);
    BGRAToYRow(src_bgra, dst_y, width);
652 653
  }
  return 0;
654 655
}

656
// Convert ABGR to I420.
657
LIBYUV_API
658
int ABGRToI420(const uint8* src_abgr, int src_stride_abgr,
659 660 661 662
               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) {
663 664
  int y;
  void (*ABGRToUVRow)(const uint8* src_abgr0, int src_stride_abgr,
665
      uint8* dst_u, uint8* dst_v, int width) = ABGRToUVRow_C;
666
  void (*ABGRToYRow)(const uint8* src_abgr, uint8* dst_y, int width) =
667
      ABGRToYRow_C;
668 669 670 671 672 673
  if (!src_abgr ||
      !dst_y || !dst_u || !dst_v ||
      width <= 0 || height == 0) {
    return -1;
  }
  // Negative height means invert the image.
674 675
  if (height < 0) {
    height = -height;
676 677
    src_abgr = src_abgr + (height - 1) * src_stride_abgr;
    src_stride_abgr = -src_stride_abgr;
678
  }
679
#if defined(HAS_ABGRTOYROW_SSSE3) && defined(HAS_ABGRTOUVROW_SSSE3)
680
  if (TestCpuFlag(kCpuHasSSSE3)) {
681 682
    ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
    ABGRToYRow = ABGRToYRow_Any_SSSE3;
683
    if (IS_ALIGNED(width, 16)) {
684 685
      ABGRToUVRow = ABGRToUVRow_SSSE3;
      ABGRToYRow = ABGRToYRow_SSSE3;
686
    }
687
  }
688 689
#endif
#if defined(HAS_ABGRTOYROW_NEON)
690
  if (TestCpuFlag(kCpuHasNEON)) {
691 692 693 694
    ABGRToYRow = ABGRToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ABGRToYRow = ABGRToYRow_NEON;
    }
695 696 697
  }
#endif
#if defined(HAS_ABGRTOUVROW_NEON)
698
  if (TestCpuFlag(kCpuHasNEON)) {
699 700 701
    ABGRToUVRow = ABGRToUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      ABGRToUVRow = ABGRToUVRow_NEON;
702
    }
703
  }
704
#endif
705

706
  for (y = 0; y < height - 1; y += 2) {
707 708 709 710
    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;
711 712 713 714 715
    dst_y += dst_stride_y * 2;
    dst_u += dst_stride_u;
    dst_v += dst_stride_v;
  }
  if (height & 1) {
716 717
    ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
    ABGRToYRow(src_abgr, dst_y, width);
718 719
  }
  return 0;
720 721
}

722
// Convert RGBA to I420.
723
LIBYUV_API
724 725 726 727 728
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) {
729 730
  int y;
  void (*RGBAToUVRow)(const uint8* src_rgba0, int src_stride_rgba,
731
      uint8* dst_u, uint8* dst_v, int width) = RGBAToUVRow_C;
732
  void (*RGBAToYRow)(const uint8* src_rgba, uint8* dst_y, int width) =
733
      RGBAToYRow_C;
734 735 736 737 738 739 740 741 742 743 744
  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;
  }
745
#if defined(HAS_RGBATOYROW_SSSE3) && defined(HAS_RGBATOUVROW_SSSE3)
746
  if (TestCpuFlag(kCpuHasSSSE3)) {
747 748
    RGBAToUVRow = RGBAToUVRow_Any_SSSE3;
    RGBAToYRow = RGBAToYRow_Any_SSSE3;
749
    if (IS_ALIGNED(width, 16)) {
750 751
      RGBAToUVRow = RGBAToUVRow_SSSE3;
      RGBAToYRow = RGBAToYRow_SSSE3;
752 753
    }
  }
754 755
#endif
#if defined(HAS_RGBATOYROW_NEON)
756
  if (TestCpuFlag(kCpuHasNEON)) {
757 758 759 760
    RGBAToYRow = RGBAToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGBAToYRow = RGBAToYRow_NEON;
    }
761 762 763
  }
#endif
#if defined(HAS_RGBATOUVROW_NEON)
764
  if (TestCpuFlag(kCpuHasNEON)) {
765 766 767
    RGBAToUVRow = RGBAToUVRow_Any_NEON;
    if (IS_ALIGNED(width, 16)) {
      RGBAToUVRow = RGBAToUVRow_NEON;
768
    }
769
  }
770 771
#endif

772
  for (y = 0; y < height - 1; y += 2) {
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
    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;
}

788
// Convert RGB24 to I420.
789
LIBYUV_API
790
int RGB24ToI420(const uint8* src_rgb24, int src_stride_rgb24,
791 792 793 794
                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) {
795 796 797 798
  int y;
#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;
799
  void (*RGB24ToYRow)(const uint8* src_rgb24, uint8* dst_y, int width) =
800 801
      RGB24ToYRow_C;
#else
802
  void (*RGB24ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
803 804
      RGB24ToARGBRow_C;
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
805
      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
806
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
807 808
      ARGBToYRow_C;
#endif
809
  if (!src_rgb24 || !dst_y || !dst_u || !dst_v ||
810
      width <= 0 || height == 0) {
811 812
    return -1;
  }
813
  // Negative height means invert the image.
814 815
  if (height < 0) {
    height = -height;
816 817
    src_rgb24 = src_rgb24 + (height - 1) * src_stride_rgb24;
    src_stride_rgb24 = -src_stride_rgb24;
818
  }
819

820
// Neon version does direct RGB24 to YUV.
821
#if defined(HAS_RGB24TOYROW_NEON)
822
  if (TestCpuFlag(kCpuHasNEON)) {
823
    RGB24ToUVRow = RGB24ToUVRow_Any_NEON;
824 825 826
    RGB24ToYRow = RGB24ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGB24ToYRow = RGB24ToYRow_NEON;
827 828 829
      if (IS_ALIGNED(width, 16)) {
        RGB24ToUVRow = RGB24ToUVRow_NEON;
      }
830
    }
831
  }
832 833
// Other platforms do intermediate conversion from RGB24 to ARGB.
#else
834
#if defined(HAS_RGB24TOARGBROW_SSSE3)
835
  if (TestCpuFlag(kCpuHasSSSE3)) {
836 837 838 839 840
    RGB24ToARGBRow = RGB24ToARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      RGB24ToARGBRow = RGB24ToARGBRow_SSSE3;
    }
  }
841
#endif
842
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
843
  if (TestCpuFlag(kCpuHasSSSE3)) {
844
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
845
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
846
    if (IS_ALIGNED(width, 16)) {
847
      ARGBToUVRow = ARGBToUVRow_SSSE3;
848
      ARGBToYRow = ARGBToYRow_SSSE3;
849
    }
850
  }
851 852
#endif
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
853
  if (TestCpuFlag(kCpuHasAVX2)) {
854
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
855 856
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
857
      ARGBToUVRow = ARGBToUVRow_AVX2;
858 859 860 861
      ARGBToYRow = ARGBToYRow_AVX2;
    }
  }
#endif
862
  {
863
    // Allocate 2 rows of ARGB.
864
    const int kRowSize = (width * 4 + 31) & ~31;
865 866 867 868
    align_buffer_64(row, kRowSize * 2);
#endif

    for (y = 0; y < height - 1; y += 2) {
869
#if defined(HAS_RGB24TOYROW_NEON)
870 871 872
      RGB24ToUVRow(src_rgb24, src_stride_rgb24, dst_u, dst_v, width);
      RGB24ToYRow(src_rgb24, dst_y, width);
      RGB24ToYRow(src_rgb24 + src_stride_rgb24, dst_y + dst_stride_y, width);
873
#else
874 875 876 877 878
      RGB24ToARGBRow(src_rgb24, row, width);
      RGB24ToARGBRow(src_rgb24 + src_stride_rgb24, row + kRowSize, width);
      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
879
#endif
880 881 882 883 884 885
      src_rgb24 += src_stride_rgb24 * 2;
      dst_y += dst_stride_y * 2;
      dst_u += dst_stride_u;
      dst_v += dst_stride_v;
    }
    if (height & 1) {
886
#if defined(HAS_RGB24TOYROW_NEON)
887 888
      RGB24ToUVRow(src_rgb24, 0, dst_u, dst_v, width);
      RGB24ToYRow(src_rgb24, dst_y, width);
889
#else
890 891 892
      RGB24ToARGBRow(src_rgb24, row, width);
      ARGBToUVRow(row, 0, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
893
#endif
894
    }
895
#if !defined(HAS_RGB24TOYROW_NEON)
896 897
    free_aligned_buffer_64(row);
  }
898
#endif
899
  return 0;
900 901
}

902
// Convert RAW to I420.
903
LIBYUV_API
904
int RAWToI420(const uint8* src_raw, int src_stride_raw,
905 906 907 908
              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) {
909 910 911 912
  int y;
#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;
913
  void (*RAWToYRow)(const uint8* src_raw, uint8* dst_y, int width) =
914 915
      RAWToYRow_C;
#else
916
  void (*RAWToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
917 918
      RAWToARGBRow_C;
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
919
      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
920
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
921 922
      ARGBToYRow_C;
#endif
923
  if (!src_raw || !dst_y || !dst_u || !dst_v ||
924
      width <= 0 || height == 0) {
925 926
    return -1;
  }
927
  // Negative height means invert the image.
928 929
  if (height < 0) {
    height = -height;
930 931
    src_raw = src_raw + (height - 1) * src_stride_raw;
    src_stride_raw = -src_stride_raw;
932
  }
933

934
// Neon version does direct RAW to YUV.
935
#if defined(HAS_RAWTOYROW_NEON)
936
  if (TestCpuFlag(kCpuHasNEON)) {
937
    RAWToUVRow = RAWToUVRow_Any_NEON;
938 939 940
    RAWToYRow = RAWToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RAWToYRow = RAWToYRow_NEON;
941 942 943
      if (IS_ALIGNED(width, 16)) {
        RAWToUVRow = RAWToUVRow_NEON;
      }
944
    }
945
  }
946 947
// Other platforms do intermediate conversion from RAW to ARGB.
#else
948
#if defined(HAS_RAWTOARGBROW_SSSE3)
949
  if (TestCpuFlag(kCpuHasSSSE3)) {
950 951 952 953 954
    RAWToARGBRow = RAWToARGBRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
      RAWToARGBRow = RAWToARGBRow_SSSE3;
    }
  }
955
#endif
956
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
957
  if (TestCpuFlag(kCpuHasSSSE3)) {
958
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
959
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
960
    if (IS_ALIGNED(width, 16)) {
961
      ARGBToUVRow = ARGBToUVRow_SSSE3;
962
      ARGBToYRow = ARGBToYRow_SSSE3;
963
    }
964
  }
965 966
#endif
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
967
  if (TestCpuFlag(kCpuHasAVX2)) {
968
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
969 970
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
971
      ARGBToUVRow = ARGBToUVRow_AVX2;
972 973 974 975
      ARGBToYRow = ARGBToYRow_AVX2;
    }
  }
#endif
976 977
  {
    // Allocate 2 rows of ARGB.
978
    const int kRowSize = (width * 4 + 31) & ~31;
979
    align_buffer_64(row, kRowSize * 2);
980
#endif
981 982

    for (y = 0; y < height - 1; y += 2) {
983
#if defined(HAS_RAWTOYROW_NEON)
984 985 986
      RAWToUVRow(src_raw, src_stride_raw, dst_u, dst_v, width);
      RAWToYRow(src_raw, dst_y, width);
      RAWToYRow(src_raw + src_stride_raw, dst_y + dst_stride_y, width);
987
#else
988 989 990 991 992
      RAWToARGBRow(src_raw, row, width);
      RAWToARGBRow(src_raw + src_stride_raw, row + kRowSize, width);
      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
993
#endif
994 995 996 997 998 999
      src_raw += src_stride_raw * 2;
      dst_y += dst_stride_y * 2;
      dst_u += dst_stride_u;
      dst_v += dst_stride_v;
    }
    if (height & 1) {
1000
#if defined(HAS_RAWTOYROW_NEON)
1001 1002
      RAWToUVRow(src_raw, 0, dst_u, dst_v, width);
      RAWToYRow(src_raw, dst_y, width);
1003
#else
1004 1005 1006
      RAWToARGBRow(src_raw, row, width);
      ARGBToUVRow(row, 0, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
1007
#endif
1008
    }
1009
#if !defined(HAS_RAWTOYROW_NEON)
1010
    free_aligned_buffer_64(row);
1011
  }
1012
#endif
1013
  return 0;
mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
1014 1015
}

1016
// Convert RGB565 to I420.
1017
LIBYUV_API
1018
int RGB565ToI420(const uint8* src_rgb565, int src_stride_rgb565,
1019 1020 1021 1022 1023 1024 1025 1026
                 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) {
  int y;
#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;
1027
  void (*RGB565ToYRow)(const uint8* src_rgb565, uint8* dst_y, int width) =
1028 1029
      RGB565ToYRow_C;
#else
1030
  void (*RGB565ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
1031 1032
      RGB565ToARGBRow_C;
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1033
      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
1034
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
1035 1036
      ARGBToYRow_C;
#endif
1037
  if (!src_rgb565 || !dst_y || !dst_u || !dst_v ||
1038
      width <= 0 || height == 0) {
1039 1040
    return -1;
  }
1041
  // Negative height means invert the image.
1042 1043
  if (height < 0) {
    height = -height;
1044 1045
    src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
    src_stride_rgb565 = -src_stride_rgb565;
1046
  }
fbarchard@google.com's avatar
fbarchard@google.com committed
1047

1048
// Neon version does direct RGB565 to YUV.
fbarchard@google.com's avatar
fbarchard@google.com committed
1049
#if defined(HAS_RGB565TOYROW_NEON)
1050
  if (TestCpuFlag(kCpuHasNEON)) {
1051
    RGB565ToUVRow = RGB565ToUVRow_Any_NEON;
fbarchard@google.com's avatar
fbarchard@google.com committed
1052 1053 1054
    RGB565ToYRow = RGB565ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      RGB565ToYRow = RGB565ToYRow_NEON;
1055 1056 1057
      if (IS_ALIGNED(width, 16)) {
        RGB565ToUVRow = RGB565ToUVRow_NEON;
      }
fbarchard@google.com's avatar
fbarchard@google.com committed
1058 1059
    }
  }
1060 1061
// Other platforms do intermediate conversion from RGB565 to ARGB.
#else
1062
#if defined(HAS_RGB565TOARGBROW_SSE2)
1063
  if (TestCpuFlag(kCpuHasSSE2)) {
1064 1065 1066 1067 1068
    RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
    }
  }
1069
#endif
1070 1071 1072 1073 1074 1075 1076 1077
#if defined(HAS_RGB565TOARGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    RGB565ToARGBRow = RGB565ToARGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      RGB565ToARGBRow = RGB565ToARGBRow_AVX2;
    }
  }
#endif
1078
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
1079
  if (TestCpuFlag(kCpuHasSSSE3)) {
1080
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1081
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
1082
    if (IS_ALIGNED(width, 16)) {
1083
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1084
      ARGBToYRow = ARGBToYRow_SSSE3;
1085
    }
1086
  }
1087
#endif
1088
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
1089
  if (TestCpuFlag(kCpuHasAVX2)) {
1090
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
1091 1092
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
1093
      ARGBToUVRow = ARGBToUVRow_AVX2;
1094 1095 1096 1097
      ARGBToYRow = ARGBToYRow_AVX2;
    }
  }
#endif
1098
  {
1099
    // Allocate 2 rows of ARGB.
1100
    const int kRowSize = (width * 4 + 31) & ~31;
1101 1102 1103 1104
    align_buffer_64(row, kRowSize * 2);
#endif

    for (y = 0; y < height - 1; y += 2) {
1105
#if defined(HAS_RGB565TOYROW_NEON)
1106 1107 1108
      RGB565ToUVRow(src_rgb565, src_stride_rgb565, dst_u, dst_v, width);
      RGB565ToYRow(src_rgb565, dst_y, width);
      RGB565ToYRow(src_rgb565 + src_stride_rgb565, dst_y + dst_stride_y, width);
1109
#else
1110 1111 1112 1113 1114
      RGB565ToARGBRow(src_rgb565, row, width);
      RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + kRowSize, width);
      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
1115
#endif
1116 1117 1118 1119 1120 1121
      src_rgb565 += src_stride_rgb565 * 2;
      dst_y += dst_stride_y * 2;
      dst_u += dst_stride_u;
      dst_v += dst_stride_v;
    }
    if (height & 1) {
1122
#if defined(HAS_RGB565TOYROW_NEON)
1123 1124
      RGB565ToUVRow(src_rgb565, 0, dst_u, dst_v, width);
      RGB565ToYRow(src_rgb565, dst_y, width);
1125
#else
1126 1127 1128
      RGB565ToARGBRow(src_rgb565, row, width);
      ARGBToUVRow(row, 0, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
1129
#endif
1130
    }
1131
#if !defined(HAS_RGB565TOYROW_NEON)
1132 1133
    free_aligned_buffer_64(row);
  }
1134
#endif
1135 1136 1137
  return 0;
}

1138
// Convert ARGB1555 to I420.
1139
LIBYUV_API
1140
int ARGB1555ToI420(const uint8* src_argb1555, int src_stride_argb1555,
1141 1142 1143 1144
                   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) {
1145 1146 1147 1148
  int y;
#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;
1149
  void (*ARGB1555ToYRow)(const uint8* src_argb1555, uint8* dst_y, int width) =
1150 1151
      ARGB1555ToYRow_C;
#else
1152
  void (*ARGB1555ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
1153 1154
      ARGB1555ToARGBRow_C;
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1155
      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
1156
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
1157 1158
      ARGBToYRow_C;
#endif
1159
  if (!src_argb1555 || !dst_y || !dst_u || !dst_v ||
1160
      width <= 0 || height == 0) {
1161 1162
    return -1;
  }
1163
  // Negative height means invert the image.
1164 1165
  if (height < 0) {
    height = -height;
1166 1167
    src_argb1555 = src_argb1555 + (height - 1) * src_stride_argb1555;
    src_stride_argb1555 = -src_stride_argb1555;
1168
  }
1169

1170
// Neon version does direct ARGB1555 to YUV.
1171
#if defined(HAS_ARGB1555TOYROW_NEON)
1172
  if (TestCpuFlag(kCpuHasNEON)) {
1173
    ARGB1555ToUVRow = ARGB1555ToUVRow_Any_NEON;
1174 1175 1176
    ARGB1555ToYRow = ARGB1555ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ARGB1555ToYRow = ARGB1555ToYRow_NEON;
1177 1178 1179
      if (IS_ALIGNED(width, 16)) {
        ARGB1555ToUVRow = ARGB1555ToUVRow_NEON;
      }
1180 1181
    }
  }
1182 1183
// Other platforms do intermediate conversion from ARGB1555 to ARGB.
#else
1184
#if defined(HAS_ARGB1555TOARGBROW_SSE2)
1185
  if (TestCpuFlag(kCpuHasSSE2)) {
1186 1187 1188 1189 1190
    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      ARGB1555ToARGBRow = ARGB1555ToARGBRow_SSE2;
    }
  }
1191
#endif
1192 1193 1194 1195 1196 1197 1198 1199
#if defined(HAS_ARGB1555TOARGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    ARGB1555ToARGBRow = ARGB1555ToARGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      ARGB1555ToARGBRow = ARGB1555ToARGBRow_AVX2;
    }
  }
#endif
1200
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
1201
  if (TestCpuFlag(kCpuHasSSSE3)) {
1202
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1203 1204
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
1205
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1206
      ARGBToYRow = ARGBToYRow_SSSE3;
1207
    }
1208
  }
1209
#endif
1210
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
1211
  if (TestCpuFlag(kCpuHasAVX2)) {
1212
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
1213 1214
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
1215
      ARGBToUVRow = ARGBToUVRow_AVX2;
1216 1217 1218 1219
      ARGBToYRow = ARGBToYRow_AVX2;
    }
  }
#endif
1220
  {
1221
    // Allocate 2 rows of ARGB.
1222
    const int kRowSize = (width * 4 + 31) & ~31;
1223 1224
    align_buffer_64(row, kRowSize * 2);
#endif
1225

1226
    for (y = 0; y < height - 1; y += 2) {
1227
#if defined(HAS_ARGB1555TOYROW_NEON)
1228 1229 1230 1231
      ARGB1555ToUVRow(src_argb1555, src_stride_argb1555, dst_u, dst_v, width);
      ARGB1555ToYRow(src_argb1555, dst_y, width);
      ARGB1555ToYRow(src_argb1555 + src_stride_argb1555, dst_y + dst_stride_y,
                     width);
1232
#else
1233 1234 1235 1236 1237 1238
      ARGB1555ToARGBRow(src_argb1555, row, width);
      ARGB1555ToARGBRow(src_argb1555 + src_stride_argb1555, row + kRowSize,
                        width);
      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
1239
#endif
1240 1241 1242 1243 1244 1245
      src_argb1555 += src_stride_argb1555 * 2;
      dst_y += dst_stride_y * 2;
      dst_u += dst_stride_u;
      dst_v += dst_stride_v;
    }
    if (height & 1) {
1246
#if defined(HAS_ARGB1555TOYROW_NEON)
1247 1248
      ARGB1555ToUVRow(src_argb1555, 0, dst_u, dst_v, width);
      ARGB1555ToYRow(src_argb1555, dst_y, width);
1249
#else
1250 1251 1252
      ARGB1555ToARGBRow(src_argb1555, row, width);
      ARGBToUVRow(row, 0, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
1253
#endif
1254
    }
1255
#if !defined(HAS_ARGB1555TOYROW_NEON)
1256
    free_aligned_buffer_64(row);
1257
  }
1258
#endif
1259 1260 1261
  return 0;
}

1262
// Convert ARGB4444 to I420.
1263
LIBYUV_API
1264
int ARGB4444ToI420(const uint8* src_argb4444, int src_stride_argb4444,
1265 1266 1267 1268
                   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) {
1269 1270 1271 1272
  int y;
#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;
1273
  void (*ARGB4444ToYRow)(const uint8* src_argb4444, uint8* dst_y, int width) =
1274 1275
      ARGB4444ToYRow_C;
#else
1276
  void (*ARGB4444ToARGBRow)(const uint8* src_rgb, uint8* dst_argb, int width) =
1277 1278
      ARGB4444ToARGBRow_C;
  void (*ARGBToUVRow)(const uint8* src_argb0, int src_stride_argb,
1279
      uint8* dst_u, uint8* dst_v, int width) = ARGBToUVRow_C;
1280
  void (*ARGBToYRow)(const uint8* src_argb, uint8* dst_y, int width) =
1281 1282
      ARGBToYRow_C;
#endif
1283
  if (!src_argb4444 || !dst_y || !dst_u || !dst_v ||
1284
      width <= 0 || height == 0) {
1285 1286
    return -1;
  }
1287
  // Negative height means invert the image.
1288 1289
  if (height < 0) {
    height = -height;
1290 1291
    src_argb4444 = src_argb4444 + (height - 1) * src_stride_argb4444;
    src_stride_argb4444 = -src_stride_argb4444;
1292
  }
1293

1294
// Neon version does direct ARGB4444 to YUV.
1295
#if defined(HAS_ARGB4444TOYROW_NEON)
1296
  if (TestCpuFlag(kCpuHasNEON)) {
1297
    ARGB4444ToUVRow = ARGB4444ToUVRow_Any_NEON;
1298 1299 1300
    ARGB4444ToYRow = ARGB4444ToYRow_Any_NEON;
    if (IS_ALIGNED(width, 8)) {
      ARGB4444ToYRow = ARGB4444ToYRow_NEON;
1301 1302 1303
      if (IS_ALIGNED(width, 16)) {
        ARGB4444ToUVRow = ARGB4444ToUVRow_NEON;
      }
1304 1305
    }
  }
1306 1307
// Other platforms do intermediate conversion from ARGB4444 to ARGB.
#else
1308
#if defined(HAS_ARGB4444TOARGBROW_SSE2)
1309
  if (TestCpuFlag(kCpuHasSSE2)) {
1310 1311 1312 1313 1314
    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_SSE2;
    if (IS_ALIGNED(width, 8)) {
      ARGB4444ToARGBRow = ARGB4444ToARGBRow_SSE2;
    }
  }
1315
#endif
1316 1317 1318 1319 1320 1321 1322 1323
#if defined(HAS_ARGB4444TOARGBROW_AVX2)
  if (TestCpuFlag(kCpuHasAVX2)) {
    ARGB4444ToARGBRow = ARGB4444ToARGBRow_Any_AVX2;
    if (IS_ALIGNED(width, 16)) {
      ARGB4444ToARGBRow = ARGB4444ToARGBRow_AVX2;
    }
  }
#endif
1324
#if defined(HAS_ARGBTOYROW_SSSE3) && defined(HAS_ARGBTOUVROW_SSSE3)
1325
  if (TestCpuFlag(kCpuHasSSSE3)) {
1326
    ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
1327 1328
    ARGBToYRow = ARGBToYRow_Any_SSSE3;
    if (IS_ALIGNED(width, 16)) {
1329
      ARGBToUVRow = ARGBToUVRow_SSSE3;
1330
      ARGBToYRow = ARGBToYRow_SSSE3;
1331
    }
1332
  }
1333
#endif
1334
#if defined(HAS_ARGBTOYROW_AVX2) && defined(HAS_ARGBTOUVROW_AVX2)
1335
  if (TestCpuFlag(kCpuHasAVX2)) {
1336
    ARGBToUVRow = ARGBToUVRow_Any_AVX2;
1337 1338
    ARGBToYRow = ARGBToYRow_Any_AVX2;
    if (IS_ALIGNED(width, 32)) {
1339
      ARGBToUVRow = ARGBToUVRow_AVX2;
1340 1341 1342 1343
      ARGBToYRow = ARGBToYRow_AVX2;
    }
  }
#endif
1344
  {
1345
    // Allocate 2 rows of ARGB.
1346
    const int kRowSize = (width * 4 + 31) & ~31;
1347 1348 1349 1350
    align_buffer_64(row, kRowSize * 2);
#endif

    for (y = 0; y < height - 1; y += 2) {
1351
#if defined(HAS_ARGB4444TOYROW_NEON)
1352 1353 1354 1355
      ARGB4444ToUVRow(src_argb4444, src_stride_argb4444, dst_u, dst_v, width);
      ARGB4444ToYRow(src_argb4444, dst_y, width);
      ARGB4444ToYRow(src_argb4444 + src_stride_argb4444, dst_y + dst_stride_y,
                     width);
1356
#else
1357 1358 1359 1360 1361 1362
      ARGB4444ToARGBRow(src_argb4444, row, width);
      ARGB4444ToARGBRow(src_argb4444 + src_stride_argb4444, row + kRowSize,
                        width);
      ARGBToUVRow(row, kRowSize, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
      ARGBToYRow(row + kRowSize, dst_y + dst_stride_y, width);
1363
#endif
1364 1365 1366 1367 1368 1369
      src_argb4444 += src_stride_argb4444 * 2;
      dst_y += dst_stride_y * 2;
      dst_u += dst_stride_u;
      dst_v += dst_stride_v;
    }
    if (height & 1) {
1370
#if defined(HAS_ARGB4444TOYROW_NEON)
1371 1372
      ARGB4444ToUVRow(src_argb4444, 0, dst_u, dst_v, width);
      ARGB4444ToYRow(src_argb4444, dst_y, width);
1373
#else
1374 1375 1376
      ARGB4444ToARGBRow(src_argb4444, row, width);
      ARGBToUVRow(row, 0, dst_u, dst_v, width);
      ARGBToYRow(row, dst_y, width);
1377
#endif
1378
    }
1379
#if !defined(HAS_ARGB4444TOYROW_NEON)
1380 1381
    free_aligned_buffer_64(row);
  }
1382
#endif
1383 1384 1385
  return 0;
}

1386 1387 1388 1389
#ifdef __cplusplus
}  // extern "C"
}  // namespace libyuv
#endif