Coverage Report

Created: 2026-10-17 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libavif/ext/libyuv/source/planar_functions.cc
Line
Count
Source
1
/*
2
 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
3
 *
4
 *  Use of this source code is governed by a BSD-style license
5
 *  that can be found in the LICENSE file in the root of the source
6
 *  tree. An additional intellectual property rights grant can be found
7
 *  in the file PATENTS. All contributing project authors may
8
 *  be found in the AUTHORS file in the root of the source tree.
9
 */
10
11
#include "libyuv/planar_functions.h"
12
13
#include <assert.h>
14
#include <limits.h>
15
#include <string.h>  // for memset()
16
17
#include "libyuv/convert_from_argb.h"  // For ArgbConstants
18
#include "libyuv/cpu_id.h"
19
#include "libyuv/row.h"
20
#include "libyuv/scale_row.h"  // for ScaleRowDown2
21
22
#ifdef __cplusplus
23
namespace libyuv {
24
extern "C" {
25
#endif
26
27
// Copy a plane of data
28
LIBYUV_API
29
void CopyPlane(const uint8_t* src_y,
30
               int src_stride_y,
31
               uint8_t* dst_y,
32
               int dst_stride_y,
33
               int width,
34
540
               int height) {
35
540
  int y;
36
540
  void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
37
540
  if (width <= 0 || height == 0 || height == INT_MIN) {
38
0
    return;
39
0
  }
40
  // Negative height means invert the image.
41
540
  if (height < 0) {
42
0
    height = -height;
43
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
44
0
    dst_stride_y = -dst_stride_y;
45
0
  }
46
  // Coalesce rows.
47
540
  if (src_stride_y == width && dst_stride_y == width &&
48
10
      (ptrdiff_t)width * height <= INT_MAX) {
49
10
    width *= height;
50
10
    height = 1;
51
10
    src_stride_y = dst_stride_y = 0;
52
10
  }
53
  // Nothing to do.
54
540
  if (src_y == dst_y && src_stride_y == dst_stride_y) {
55
0
    return;
56
0
  }
57
58
540
#if defined(HAS_COPYROW_SSE2)
59
540
  if (TestCpuFlag(kCpuHasSSE2)) {
60
540
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
61
540
  }
62
540
#endif
63
540
#if defined(HAS_COPYROW_AVX)
64
540
  if (TestCpuFlag(kCpuHasAVX)) {
65
540
    CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
66
540
  }
67
540
#endif
68
540
#if defined(HAS_COPYROW_AVX512BW)
69
540
  if (TestCpuFlag(kCpuHasAVX512BW)) {
70
0
    CopyRow = IS_ALIGNED(width, 128) ? CopyRow_AVX512BW : CopyRow_Any_AVX512BW;
71
0
  }
72
540
#endif
73
540
#if defined(HAS_COPYROW_ERMS)
74
540
  if (TestCpuFlag(kCpuHasERMS)) {
75
0
    CopyRow = CopyRow_ERMS;
76
0
  }
77
540
#endif
78
#if defined(HAS_COPYROW_NEON)
79
  if (TestCpuFlag(kCpuHasNEON)) {
80
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
81
  }
82
#endif
83
#if defined(HAS_COPYROW_SVE2)
84
  if (TestCpuFlag(kCpuHasSVE2)) {
85
    CopyRow = CopyRow_SVE2;
86
  }
87
#endif
88
#if defined(HAS_COPYROW_SME)
89
  if (TestCpuFlag(kCpuHasSME)) {
90
    CopyRow = CopyRow_SME;
91
  }
92
#endif
93
#if defined(HAS_COPYROW_RVV)
94
  if (TestCpuFlag(kCpuHasRVV)) {
95
    CopyRow = CopyRow_RVV;
96
  }
97
#endif
98
99
  // Copy plane
100
21.5k
  for (y = 0; y < height; ++y) {
101
20.9k
    CopyRow(src_y, dst_y, width);
102
20.9k
    src_y += src_stride_y;
103
20.9k
    dst_y += dst_stride_y;
104
20.9k
  }
105
540
}
106
107
LIBYUV_API
108
void CopyPlane_16(const uint16_t* src_y,
109
                  int src_stride_y,
110
                  uint16_t* dst_y,
111
                  int dst_stride_y,
112
                  int width,
113
244
                  int height) {
114
244
  CopyPlane((const uint8_t*)src_y, src_stride_y * 2, (uint8_t*)dst_y,
115
244
            dst_stride_y * 2, width * 2, height);
116
244
}
117
118
// Convert a plane of 16 bit data to 8 bit
119
LIBYUV_API
120
void Convert16To8Plane(const uint16_t* src_y,
121
                       int src_stride_y,
122
                       uint8_t* dst_y,
123
                       int dst_stride_y,
124
                       int scale,  // 16384 for 10 bits
125
                       int width,
126
622
                       int height) {
127
622
  int y;
128
622
  void (*Convert16To8Row)(const uint16_t* src_y, uint8_t* dst_y, int scale,
129
622
                          int width) = Convert16To8Row_C;
130
131
622
  if (width <= 0 || height == 0 || height == INT_MIN) {
132
0
    return;
133
0
  }
134
  // Negative height means invert the image.
135
622
  if (height < 0) {
136
0
    height = -height;
137
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
138
0
    dst_stride_y = -dst_stride_y;
139
0
  }
140
  // Coalesce rows.
141
622
  if (src_stride_y == width && dst_stride_y == width &&
142
622
      (ptrdiff_t)width * height <= INT_MAX) {
143
622
    width *= height;
144
622
    height = 1;
145
622
    src_stride_y = dst_stride_y = 0;
146
622
  }
147
#if defined(HAS_CONVERT16TO8ROW_NEON)
148
  if (TestCpuFlag(kCpuHasNEON)) {
149
    Convert16To8Row = Convert16To8Row_Any_NEON;
150
    if (IS_ALIGNED(width, 16)) {
151
      Convert16To8Row = Convert16To8Row_NEON;
152
    }
153
  }
154
#endif
155
#if defined(HAS_CONVERT16TO8ROW_SME)
156
  if (TestCpuFlag(kCpuHasSME)) {
157
    Convert16To8Row = Convert16To8Row_SME;
158
  }
159
#endif
160
622
#if defined(HAS_CONVERT16TO8ROW_SSSE3)
161
622
  if (TestCpuFlag(kCpuHasSSSE3)) {
162
622
    Convert16To8Row = Convert16To8Row_Any_SSSE3;
163
622
    if (IS_ALIGNED(width, 16)) {
164
142
      Convert16To8Row = Convert16To8Row_SSSE3;
165
142
    }
166
622
  }
167
622
#endif
168
622
#if defined(HAS_CONVERT16TO8ROW_AVX2)
169
622
  if (TestCpuFlag(kCpuHasAVX2)) {
170
622
    Convert16To8Row = Convert16To8Row_Any_AVX2;
171
622
    if (IS_ALIGNED(width, 32)) {
172
70
      Convert16To8Row = Convert16To8Row_AVX2;
173
70
    }
174
622
  }
175
622
#endif
176
622
#if defined(HAS_CONVERT16TO8ROW_AVX512BW)
177
622
  if (TestCpuFlag(kCpuHasAVX512BW)) {
178
0
    Convert16To8Row = Convert16To8Row_Any_AVX512BW;
179
0
    if (IS_ALIGNED(width, 64)) {
180
0
      Convert16To8Row = Convert16To8Row_AVX512BW;
181
0
    }
182
0
  }
183
622
#endif
184
185
  // Convert plane
186
1.24k
  for (y = 0; y < height; ++y) {
187
622
    Convert16To8Row(src_y, dst_y, scale, width);
188
622
    src_y += src_stride_y;
189
622
    dst_y += dst_stride_y;
190
622
  }
191
622
}
192
193
// Convert a plane of 8 bit data to 16 bit
194
LIBYUV_API
195
void Convert8To16Plane(const uint8_t* src_y,
196
                       int src_stride_y,
197
                       uint16_t* dst_y,
198
                       int dst_stride_y,
199
                       int scale,  // 1024 for 10 bits
200
                       int width,
201
0
                       int height) {
202
0
  int y;
203
0
  void (*Convert8To16Row)(const uint8_t* src_y, uint16_t* dst_y, int scale,
204
0
                          int width) = Convert8To16Row_C;
205
206
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
207
0
    return;
208
0
  }
209
  // Negative height means invert the image.
210
0
  if (height < 0) {
211
0
    height = -height;
212
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
213
0
    dst_stride_y = -dst_stride_y;
214
0
  }
215
  // Coalesce rows.
216
0
  if (src_stride_y == width && dst_stride_y == width &&
217
0
      (ptrdiff_t)width * height <= INT_MAX) {
218
0
    width *= height;
219
0
    height = 1;
220
0
    src_stride_y = dst_stride_y = 0;
221
0
  }
222
0
#if defined(HAS_CONVERT8TO16ROW_SSE2)
223
0
  if (TestCpuFlag(kCpuHasSSE2)) {
224
0
    Convert8To16Row = Convert8To16Row_Any_SSE2;
225
0
    if (IS_ALIGNED(width, 16)) {
226
0
      Convert8To16Row = Convert8To16Row_SSE2;
227
0
    }
228
0
  }
229
0
#endif
230
0
#if defined(HAS_CONVERT8TO16ROW_AVX2)
231
0
  if (TestCpuFlag(kCpuHasAVX2)) {
232
0
    Convert8To16Row = Convert8To16Row_Any_AVX2;
233
0
    if (IS_ALIGNED(width, 32)) {
234
0
      Convert8To16Row = Convert8To16Row_AVX2;
235
0
    }
236
0
  }
237
0
#endif
238
#if defined(HAS_CONVERT8TO16ROW_NEON)
239
  if (TestCpuFlag(kCpuHasNEON)) {
240
    Convert8To16Row = Convert8To16Row_Any_NEON;
241
    if (IS_ALIGNED(width, 16)) {
242
      Convert8To16Row = Convert8To16Row_NEON;
243
    }
244
  }
245
#endif
246
#if defined(HAS_CONVERT8TO16ROW_SME)
247
  if (TestCpuFlag(kCpuHasSME)) {
248
    Convert8To16Row = Convert8To16Row_SME;
249
  }
250
#endif
251
252
  // Convert plane
253
0
  for (y = 0; y < height; ++y) {
254
0
    Convert8To16Row(src_y, dst_y, scale, width);
255
0
    src_y += src_stride_y;
256
0
    dst_y += dst_stride_y;
257
0
  }
258
0
}
259
260
// Convert a plane of 8 bit data to 8 bit
261
LIBYUV_API
262
void Convert8To8Plane(const uint8_t* src_y,
263
                      int src_stride_y,
264
                      uint8_t* dst_y,
265
                      int dst_stride_y,
266
                      int scale,  // 220 for Y, 225 to UV
267
                      int bias,   // 16
268
                      int width,
269
0
                      int height) {
270
0
  int y;
271
0
  void (*Convert8To8Row)(const uint8_t* src_y, uint8_t* dst_y, int scale,
272
0
                         int bias, int width) = Convert8To8Row_C;
273
274
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
275
0
    return;
276
0
  }
277
  // Negative height means invert the image.
278
0
  if (height < 0) {
279
0
    height = -height;
280
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
281
0
    dst_stride_y = -dst_stride_y;
282
0
  }
283
  // Coalesce rows.
284
0
  if (src_stride_y == width && dst_stride_y == width &&
285
0
      (ptrdiff_t)width * height <= INT_MAX) {
286
0
    width *= height;
287
0
    height = 1;
288
0
    src_stride_y = dst_stride_y = 0;
289
0
  }
290
#if defined(HAS_CONVERT8TO8ROW_NEON)
291
  if (TestCpuFlag(kCpuHasNEON)) {
292
    Convert8To8Row = Convert8To8Row_Any_NEON;
293
    if (IS_ALIGNED(width, 32)) {
294
      Convert8To8Row = Convert8To8Row_NEON;
295
    }
296
  }
297
#endif
298
#if defined(HAS_CONVERT8TO8ROW_SVE2)
299
  if (TestCpuFlag(kCpuHasSVE2)) {
300
    Convert8To8Row = Convert8To8Row_SVE2;
301
  }
302
#endif
303
#if defined(HAS_CONVERT8TO8ROW_SME)
304
  if (TestCpuFlag(kCpuHasSME)) {
305
    Convert8To8Row = Convert8To8Row_SME;
306
  }
307
#endif
308
0
#if defined(HAS_CONVERT8TO8ROW_AVX2)
309
0
  if (TestCpuFlag(kCpuHasAVX2)) {
310
0
    Convert8To8Row = Convert8To8Row_Any_AVX2;
311
0
    if (IS_ALIGNED(width, 32)) {
312
0
      Convert8To8Row = Convert8To8Row_AVX2;
313
0
    }
314
0
  }
315
0
#endif
316
317
  // Convert plane
318
0
  for (y = 0; y < height; ++y) {
319
0
    Convert8To8Row(src_y, dst_y, scale, bias, width);
320
0
    src_y += src_stride_y;
321
0
    dst_y += dst_stride_y;
322
0
  }
323
0
}
324
325
// Copy I422.
326
LIBYUV_API
327
int I422Copy(const uint8_t* src_y,
328
             int src_stride_y,
329
             const uint8_t* src_u,
330
             int src_stride_u,
331
             const uint8_t* src_v,
332
             int src_stride_v,
333
             uint8_t* dst_y,
334
             int dst_stride_y,
335
             uint8_t* dst_u,
336
             int dst_stride_u,
337
             uint8_t* dst_v,
338
             int dst_stride_v,
339
             int width,
340
0
             int height) {
341
0
  int halfwidth = (width + 1) >> 1;
342
343
0
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
344
0
      height == 0 || height == INT_MIN) {
345
0
    return -1;
346
0
  }
347
348
  // Negative height means invert the image.
349
0
  if (height < 0) {
350
0
    height = -height;
351
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
352
0
    src_u = src_u + (ptrdiff_t)(height - 1) * src_stride_u;
353
0
    src_v = src_v + (ptrdiff_t)(height - 1) * src_stride_v;
354
0
    src_stride_y = -src_stride_y;
355
0
    src_stride_u = -src_stride_u;
356
0
    src_stride_v = -src_stride_v;
357
0
  }
358
359
0
  if (dst_y) {
360
0
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
361
0
  }
362
0
  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
363
0
  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
364
0
  return 0;
365
0
}
366
367
// Copy I444.
368
LIBYUV_API
369
int I444Copy(const uint8_t* src_y,
370
             int src_stride_y,
371
             const uint8_t* src_u,
372
             int src_stride_u,
373
             const uint8_t* src_v,
374
             int src_stride_v,
375
             uint8_t* dst_y,
376
             int dst_stride_y,
377
             uint8_t* dst_u,
378
             int dst_stride_u,
379
             uint8_t* dst_v,
380
             int dst_stride_v,
381
             int width,
382
0
             int height) {
383
0
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
384
0
      height == 0 || height == INT_MIN) {
385
0
    return -1;
386
0
  }
387
  // Negative height means invert the image.
388
0
  if (height < 0) {
389
0
    height = -height;
390
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
391
0
    src_u = src_u + (ptrdiff_t)(height - 1) * src_stride_u;
392
0
    src_v = src_v + (ptrdiff_t)(height - 1) * src_stride_v;
393
0
    src_stride_y = -src_stride_y;
394
0
    src_stride_u = -src_stride_u;
395
0
    src_stride_v = -src_stride_v;
396
0
  }
397
398
0
  if (dst_y) {
399
0
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
400
0
  }
401
0
  CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
402
0
  CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
403
0
  return 0;
404
0
}
405
406
// Copy I210.
407
LIBYUV_API
408
int I210Copy(const uint16_t* src_y,
409
             int src_stride_y,
410
             const uint16_t* src_u,
411
             int src_stride_u,
412
             const uint16_t* src_v,
413
             int src_stride_v,
414
             uint16_t* dst_y,
415
             int dst_stride_y,
416
             uint16_t* dst_u,
417
             int dst_stride_u,
418
             uint16_t* dst_v,
419
             int dst_stride_v,
420
             int width,
421
0
             int height) {
422
0
  int halfwidth = (width + 1) >> 1;
423
424
0
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
425
0
      height == 0 || height == INT_MIN) {
426
0
    return -1;
427
0
  }
428
429
  // Negative height means invert the image.
430
0
  if (height < 0) {
431
0
    height = -height;
432
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
433
0
    src_u = src_u + (ptrdiff_t)(height - 1) * src_stride_u;
434
0
    src_v = src_v + (ptrdiff_t)(height - 1) * src_stride_v;
435
0
    src_stride_y = -src_stride_y;
436
0
    src_stride_u = -src_stride_u;
437
0
    src_stride_v = -src_stride_v;
438
0
  }
439
440
0
  if (dst_y) {
441
0
    CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
442
0
  }
443
  // Copy UV planes.
444
0
  CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
445
0
  CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
446
0
  return 0;
447
0
}
448
449
// Copy I410.
450
LIBYUV_API
451
int I410Copy(const uint16_t* src_y,
452
             int src_stride_y,
453
             const uint16_t* src_u,
454
             int src_stride_u,
455
             const uint16_t* src_v,
456
             int src_stride_v,
457
             uint16_t* dst_y,
458
             int dst_stride_y,
459
             uint16_t* dst_u,
460
             int dst_stride_u,
461
             uint16_t* dst_v,
462
             int dst_stride_v,
463
             int width,
464
0
             int height) {
465
0
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
466
0
      height == 0 || height == INT_MIN) {
467
0
    return -1;
468
0
  }
469
  // Negative height means invert the image.
470
0
  if (height < 0) {
471
0
    height = -height;
472
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
473
0
    src_u = src_u + (ptrdiff_t)(height - 1) * src_stride_u;
474
0
    src_v = src_v + (ptrdiff_t)(height - 1) * src_stride_v;
475
0
    src_stride_y = -src_stride_y;
476
0
    src_stride_u = -src_stride_u;
477
0
    src_stride_v = -src_stride_v;
478
0
  }
479
480
0
  if (dst_y) {
481
0
    CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
482
0
  }
483
0
  CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
484
0
  CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
485
0
  return 0;
486
0
}
487
488
// Copy I400.
489
LIBYUV_API
490
int I400ToI400(const uint8_t* src_y,
491
               int src_stride_y,
492
               uint8_t* dst_y,
493
               int dst_stride_y,
494
               int width,
495
0
               int height) {
496
0
  if (!src_y || !dst_y || width <= 0 || height == 0 || height == INT_MIN) {
497
0
    return -1;
498
0
  }
499
  // Negative height means invert the image.
500
0
  if (height < 0) {
501
0
    height = -height;
502
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
503
0
    src_stride_y = -src_stride_y;
504
0
  }
505
0
  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
506
0
  return 0;
507
0
}
508
509
// Convert I420 to I400.
510
LIBYUV_API
511
int I420ToI400(const uint8_t* src_y,
512
               int src_stride_y,
513
               const uint8_t* src_u,
514
               int src_stride_u,
515
               const uint8_t* src_v,
516
               int src_stride_v,
517
               uint8_t* dst_y,
518
               int dst_stride_y,
519
               int width,
520
0
               int height) {
521
0
  (void)src_u;
522
0
  (void)src_stride_u;
523
0
  (void)src_v;
524
0
  (void)src_stride_v;
525
0
  if (!src_y || !dst_y || width <= 0 || height == 0 || height == INT_MIN) {
526
0
    return -1;
527
0
  }
528
  // Negative height means invert the image.
529
0
  if (height < 0) {
530
0
    height = -height;
531
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
532
0
    src_stride_y = -src_stride_y;
533
0
  }
534
535
0
  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
536
0
  return 0;
537
0
}
538
539
// Copy NV12. Supports inverting.
540
LIBYUV_API
541
int NV12Copy(const uint8_t* src_y,
542
             int src_stride_y,
543
             const uint8_t* src_uv,
544
             int src_stride_uv,
545
             uint8_t* dst_y,
546
             int dst_stride_y,
547
             uint8_t* dst_uv,
548
             int dst_stride_uv,
549
             int width,
550
0
             int height) {
551
0
  int halfwidth = (width + 1) >> 1;
552
0
  int halfheight = (height + 1) >> 1;
553
554
0
  if (!src_y || !dst_y || !src_uv || !dst_uv || width <= 0 || height == 0 ||
555
0
      height == INT_MIN) {
556
0
    return -1;
557
0
  }
558
559
  // Negative height means invert the image.
560
0
  if (height < 0) {
561
0
    height = -height;
562
0
    halfheight = (height + 1) >> 1;
563
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
564
0
    src_uv = src_uv + (ptrdiff_t)(halfheight - 1) * src_stride_uv;
565
0
    src_stride_y = -src_stride_y;
566
0
    src_stride_uv = -src_stride_uv;
567
0
  }
568
0
  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
569
0
  CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, halfwidth * 2,
570
0
            halfheight);
571
0
  return 0;
572
0
}
573
574
// Copy NV21. Supports inverting.
575
LIBYUV_API
576
int NV21Copy(const uint8_t* src_y,
577
             int src_stride_y,
578
             const uint8_t* src_vu,
579
             int src_stride_vu,
580
             uint8_t* dst_y,
581
             int dst_stride_y,
582
             uint8_t* dst_vu,
583
             int dst_stride_vu,
584
             int width,
585
0
             int height) {
586
0
  return NV12Copy(src_y, src_stride_y, src_vu, src_stride_vu, dst_y,
587
0
                  dst_stride_y, dst_vu, dst_stride_vu, width, height);
588
0
}
589
590
// Support function for NV12 etc UV channels.
591
// Width and height are plane sizes (typically half pixel width).
592
LIBYUV_API
593
void SplitUVPlane(const uint8_t* src_uv,
594
                  int src_stride_uv,
595
                  uint8_t* dst_u,
596
                  int dst_stride_u,
597
                  uint8_t* dst_v,
598
                  int dst_stride_v,
599
                  int width,
600
0
                  int height) {
601
0
  int y;
602
0
  void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
603
0
                     int width) = SplitUVRow_C;
604
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
605
0
    return;
606
0
  }
607
  // Negative height means invert the image.
608
0
  if (height < 0) {
609
0
    height = -height;
610
0
    dst_u = dst_u + (ptrdiff_t)(height - 1) * dst_stride_u;
611
0
    dst_v = dst_v + (ptrdiff_t)(height - 1) * dst_stride_v;
612
0
    dst_stride_u = -dst_stride_u;
613
0
    dst_stride_v = -dst_stride_v;
614
0
  }
615
  // Coalesce rows.
616
0
  if (src_stride_uv == width * 2 && dst_stride_u == width &&
617
0
      dst_stride_v == width && (ptrdiff_t)width * height <= INT_MAX) {
618
0
    width *= height;
619
0
    height = 1;
620
0
    src_stride_uv = dst_stride_u = dst_stride_v = 0;
621
0
  }
622
0
#if defined(HAS_SPLITUVROW_SSE2)
623
0
  if (TestCpuFlag(kCpuHasSSE2)) {
624
0
    SplitUVRow = SplitUVRow_Any_SSE2;
625
0
    if (IS_ALIGNED(width, 16)) {
626
0
      SplitUVRow = SplitUVRow_SSE2;
627
0
    }
628
0
  }
629
0
#endif
630
0
#if defined(HAS_SPLITUVROW_AVX2)
631
0
  if (TestCpuFlag(kCpuHasAVX2)) {
632
0
    SplitUVRow = SplitUVRow_Any_AVX2;
633
0
    if (IS_ALIGNED(width, 32)) {
634
0
      SplitUVRow = SplitUVRow_AVX2;
635
0
    }
636
0
  }
637
0
#endif
638
0
#if defined(HAS_SPLITUVROW_AVX512BW)
639
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
640
0
    SplitUVRow = SplitUVRow_Any_AVX512BW;
641
0
    if (IS_ALIGNED(width, 64)) {
642
0
      SplitUVRow = SplitUVRow_AVX512BW;
643
0
    }
644
0
  }
645
0
#endif
646
#if defined(HAS_SPLITUVROW_NEON)
647
  if (TestCpuFlag(kCpuHasNEON)) {
648
    SplitUVRow = SplitUVRow_Any_NEON;
649
    if (IS_ALIGNED(width, 16)) {
650
      SplitUVRow = SplitUVRow_NEON;
651
    }
652
  }
653
#endif
654
#if defined(HAS_SPLITUVROW_SVE2)
655
  if (TestCpuFlag(kCpuHasSVE2)) {
656
    SplitUVRow = SplitUVRow_SVE2;
657
  }
658
#endif
659
#if defined(HAS_SPLITUVROW_LSX)
660
  if (TestCpuFlag(kCpuHasLSX)) {
661
    SplitUVRow = SplitUVRow_Any_LSX;
662
    if (IS_ALIGNED(width, 32)) {
663
      SplitUVRow = SplitUVRow_LSX;
664
    }
665
  }
666
#endif
667
#if defined(HAS_SPLITUVROW_RVV)
668
  if (TestCpuFlag(kCpuHasRVV)) {
669
    SplitUVRow = SplitUVRow_RVV;
670
  }
671
#endif
672
673
0
  for (y = 0; y < height; ++y) {
674
    // Copy a row of UV.
675
0
    SplitUVRow(src_uv, dst_u, dst_v, width);
676
0
    dst_u += dst_stride_u;
677
0
    dst_v += dst_stride_v;
678
0
    src_uv += src_stride_uv;
679
0
  }
680
0
}
681
682
LIBYUV_API
683
void MergeUVPlane(const uint8_t* src_u,
684
                  int src_stride_u,
685
                  const uint8_t* src_v,
686
                  int src_stride_v,
687
                  uint8_t* dst_uv,
688
                  int dst_stride_uv,
689
                  int width,
690
0
                  int height) {
691
0
  int y;
692
0
  void (*MergeUVRow)(const uint8_t* src_u, const uint8_t* src_v,
693
0
                     uint8_t* dst_uv, int width) = MergeUVRow_C;
694
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
695
0
    return;
696
0
  }
697
  // Negative height means invert the image.
698
0
  if (height < 0) {
699
0
    height = -height;
700
0
    dst_uv = dst_uv + (ptrdiff_t)(height - 1) * dst_stride_uv;
701
0
    dst_stride_uv = -dst_stride_uv;
702
0
  }
703
  // Coalesce rows.
704
0
  if (src_stride_u == width && src_stride_v == width &&
705
0
      dst_stride_uv == width * 2 && (ptrdiff_t)width * height <= INT_MAX) {
706
0
    width *= height;
707
0
    height = 1;
708
0
    src_stride_u = src_stride_v = dst_stride_uv = 0;
709
0
  }
710
0
#if defined(HAS_MERGEUVROW_SSE2)
711
0
  if (TestCpuFlag(kCpuHasSSE2)) {
712
0
    MergeUVRow = MergeUVRow_Any_SSE2;
713
0
    if (IS_ALIGNED(width, 16)) {
714
0
      MergeUVRow = MergeUVRow_SSE2;
715
0
    }
716
0
  }
717
0
#endif
718
0
#if defined(HAS_MERGEUVROW_AVX2)
719
0
  if (TestCpuFlag(kCpuHasAVX2)) {
720
0
    MergeUVRow = MergeUVRow_Any_AVX2;
721
0
    if (IS_ALIGNED(width, 32)) {
722
0
      MergeUVRow = MergeUVRow_AVX2;
723
0
    }
724
0
  }
725
0
#endif
726
0
#if defined(HAS_MERGEUVROW_AVX512BW)
727
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
728
0
    MergeUVRow = MergeUVRow_Any_AVX512BW;
729
0
    if (IS_ALIGNED(width, 32)) {
730
0
      MergeUVRow = MergeUVRow_AVX512BW;
731
0
    }
732
0
  }
733
0
#endif
734
#if defined(HAS_MERGEUVROW_NEON)
735
  if (TestCpuFlag(kCpuHasNEON)) {
736
    MergeUVRow = MergeUVRow_Any_NEON;
737
    if (IS_ALIGNED(width, 16)) {
738
      MergeUVRow = MergeUVRow_NEON;
739
    }
740
  }
741
#endif
742
#if defined(HAS_MERGEUVROW_SVE2)
743
  if (TestCpuFlag(kCpuHasSVE2)) {
744
    MergeUVRow = MergeUVRow_SVE2;
745
  }
746
#endif
747
#if defined(HAS_MERGEUVROW_SME)
748
  if (TestCpuFlag(kCpuHasSME)) {
749
    MergeUVRow = MergeUVRow_SME;
750
  }
751
#endif
752
#if defined(HAS_MERGEUVROW_LSX)
753
  if (TestCpuFlag(kCpuHasLSX)) {
754
    MergeUVRow = MergeUVRow_Any_LSX;
755
    if (IS_ALIGNED(width, 16)) {
756
      MergeUVRow = MergeUVRow_LSX;
757
    }
758
  }
759
#endif
760
#if defined(HAS_MERGEUVROW_RVV)
761
  if (TestCpuFlag(kCpuHasRVV)) {
762
    MergeUVRow = MergeUVRow_RVV;
763
  }
764
#endif
765
766
0
  for (y = 0; y < height; ++y) {
767
    // Merge a row of U and V into a row of UV.
768
0
    MergeUVRow(src_u, src_v, dst_uv, width);
769
0
    src_u += src_stride_u;
770
0
    src_v += src_stride_v;
771
0
    dst_uv += dst_stride_uv;
772
0
  }
773
0
}
774
775
// Support function for P010 etc UV channels.
776
// Width and height are plane sizes (typically half pixel width).
777
LIBYUV_API
778
void SplitUVPlane_16(const uint16_t* src_uv,
779
                     int src_stride_uv,
780
                     uint16_t* dst_u,
781
                     int dst_stride_u,
782
                     uint16_t* dst_v,
783
                     int dst_stride_v,
784
                     int width,
785
                     int height,
786
0
                     int depth) {
787
0
  int y;
788
0
  void (*SplitUVRow_16)(const uint16_t* src_uv, uint16_t* dst_u,
789
0
                        uint16_t* dst_v, int depth, int width) =
790
0
      SplitUVRow_16_C;
791
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
792
0
    return;
793
0
  }
794
  // Negative height means invert the image.
795
0
  if (height < 0) {
796
0
    height = -height;
797
0
    dst_u = dst_u + (ptrdiff_t)(height - 1) * dst_stride_u;
798
0
    dst_v = dst_v + (ptrdiff_t)(height - 1) * dst_stride_v;
799
0
    dst_stride_u = -dst_stride_u;
800
0
    dst_stride_v = -dst_stride_v;
801
0
  }
802
  // Coalesce rows.
803
0
  if (src_stride_uv == width * 2 && dst_stride_u == width &&
804
0
      dst_stride_v == width && (ptrdiff_t)width * height <= INT_MAX) {
805
0
    width *= height;
806
0
    height = 1;
807
0
    src_stride_uv = dst_stride_u = dst_stride_v = 0;
808
0
  }
809
0
#if defined(HAS_SPLITUVROW_16_AVX2)
810
0
  if (TestCpuFlag(kCpuHasAVX2)) {
811
0
    SplitUVRow_16 = SplitUVRow_16_Any_AVX2;
812
0
    if (IS_ALIGNED(width, 16)) {
813
0
      SplitUVRow_16 = SplitUVRow_16_AVX2;
814
0
    }
815
0
  }
816
0
#endif
817
#if defined(HAS_SPLITUVROW_16_NEON)
818
  if (TestCpuFlag(kCpuHasNEON)) {
819
    SplitUVRow_16 = SplitUVRow_16_Any_NEON;
820
    if (IS_ALIGNED(width, 8)) {
821
      SplitUVRow_16 = SplitUVRow_16_NEON;
822
    }
823
  }
824
#endif
825
826
0
  for (y = 0; y < height; ++y) {
827
    // Copy a row of UV.
828
0
    SplitUVRow_16(src_uv, dst_u, dst_v, depth, width);
829
0
    dst_u += dst_stride_u;
830
0
    dst_v += dst_stride_v;
831
0
    src_uv += src_stride_uv;
832
0
  }
833
0
}
834
835
LIBYUV_API
836
void MergeUVPlane_16(const uint16_t* src_u,
837
                     int src_stride_u,
838
                     const uint16_t* src_v,
839
                     int src_stride_v,
840
                     uint16_t* dst_uv,
841
                     int dst_stride_uv,
842
                     int width,
843
                     int height,
844
0
                     int depth) {
845
0
  int y;
846
0
  void (*MergeUVRow_16)(const uint16_t* src_u, const uint16_t* src_v,
847
0
                        uint16_t* dst_uv, int depth, int width) =
848
0
      MergeUVRow_16_C;
849
0
  assert(depth >= 8);
850
0
  assert(depth <= 16);
851
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
852
0
    return;
853
0
  }
854
  // Negative height means invert the image.
855
0
  if (height < 0) {
856
0
    height = -height;
857
0
    dst_uv = dst_uv + (ptrdiff_t)(height - 1) * dst_stride_uv;
858
0
    dst_stride_uv = -dst_stride_uv;
859
0
  }
860
  // Coalesce rows.
861
0
  if (src_stride_u == width && src_stride_v == width &&
862
0
      dst_stride_uv == width * 2 && (ptrdiff_t)width * height <= INT_MAX) {
863
0
    width *= height;
864
0
    height = 1;
865
0
    src_stride_u = src_stride_v = dst_stride_uv = 0;
866
0
  }
867
0
#if defined(HAS_MERGEUVROW_16_AVX2)
868
0
  if (TestCpuFlag(kCpuHasAVX2)) {
869
0
    MergeUVRow_16 = MergeUVRow_16_Any_AVX2;
870
0
    if (IS_ALIGNED(width, 8)) {
871
0
      MergeUVRow_16 = MergeUVRow_16_AVX2;
872
0
    }
873
0
  }
874
0
#endif
875
#if defined(HAS_MERGEUVROW_16_NEON)
876
  if (TestCpuFlag(kCpuHasNEON)) {
877
    MergeUVRow_16 = MergeUVRow_16_Any_NEON;
878
    if (IS_ALIGNED(width, 8)) {
879
      MergeUVRow_16 = MergeUVRow_16_NEON;
880
    }
881
  }
882
#endif
883
#if defined(HAS_MERGEUVROW_16_SME)
884
  if (TestCpuFlag(kCpuHasSME)) {
885
    MergeUVRow_16 = MergeUVRow_16_SME;
886
  }
887
#endif
888
889
0
  for (y = 0; y < height; ++y) {
890
    // Merge a row of U and V into a row of UV.
891
0
    MergeUVRow_16(src_u, src_v, dst_uv, depth, width);
892
0
    src_u += src_stride_u;
893
0
    src_v += src_stride_v;
894
0
    dst_uv += dst_stride_uv;
895
0
  }
896
0
}
897
898
// Convert plane from lsb to msb
899
LIBYUV_API
900
void ConvertToMSBPlane_16(const uint16_t* src_y,
901
                          int src_stride_y,
902
                          uint16_t* dst_y,
903
                          int dst_stride_y,
904
                          int width,
905
                          int height,
906
0
                          int depth) {
907
0
  int y;
908
0
  int scale = 1 << (16 - depth);
909
0
  void (*MultiplyRow_16)(const uint16_t* src_y, uint16_t* dst_y, int scale,
910
0
                         int width) = MultiplyRow_16_C;
911
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
912
0
    return;
913
0
  }
914
  // Negative height means invert the image.
915
0
  if (height < 0) {
916
0
    height = -height;
917
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
918
0
    dst_stride_y = -dst_stride_y;
919
0
  }
920
  // Coalesce rows.
921
0
  if (src_stride_y == width && dst_stride_y == width &&
922
0
      (ptrdiff_t)width * height <= INT_MAX) {
923
0
    width *= height;
924
0
    height = 1;
925
0
    src_stride_y = dst_stride_y = 0;
926
0
  }
927
928
0
#if defined(HAS_MULTIPLYROW_16_AVX2)
929
0
  if (TestCpuFlag(kCpuHasAVX2)) {
930
0
    MultiplyRow_16 = MultiplyRow_16_Any_AVX2;
931
0
    if (IS_ALIGNED(width, 32)) {
932
0
      MultiplyRow_16 = MultiplyRow_16_AVX2;
933
0
    }
934
0
  }
935
0
#endif
936
#if defined(HAS_MULTIPLYROW_16_NEON)
937
  if (TestCpuFlag(kCpuHasNEON)) {
938
    MultiplyRow_16 = MultiplyRow_16_Any_NEON;
939
    if (IS_ALIGNED(width, 16)) {
940
      MultiplyRow_16 = MultiplyRow_16_NEON;
941
    }
942
  }
943
#endif
944
#if defined(HAS_MULTIPLYROW_16_SME)
945
  if (TestCpuFlag(kCpuHasSME)) {
946
    MultiplyRow_16 = MultiplyRow_16_SME;
947
  }
948
#endif
949
950
0
  for (y = 0; y < height; ++y) {
951
0
    MultiplyRow_16(src_y, dst_y, scale, width);
952
0
    src_y += src_stride_y;
953
0
    dst_y += dst_stride_y;
954
0
  }
955
0
}
956
957
// Convert plane from msb to lsb
958
LIBYUV_API
959
void ConvertToLSBPlane_16(const uint16_t* src_y,
960
                          int src_stride_y,
961
                          uint16_t* dst_y,
962
                          int dst_stride_y,
963
                          int width,
964
                          int height,
965
0
                          int depth) {
966
0
  int y;
967
0
  int scale = 1 << depth;
968
0
  void (*DivideRow)(const uint16_t* src_y, uint16_t* dst_y, int scale,
969
0
                    int width) = DivideRow_16_C;
970
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
971
0
    return;
972
0
  }
973
  // Negative height means invert the image.
974
0
  if (height < 0) {
975
0
    height = -height;
976
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
977
0
    dst_stride_y = -dst_stride_y;
978
0
  }
979
  // Coalesce rows.
980
0
  if (src_stride_y == width && dst_stride_y == width &&
981
0
      (ptrdiff_t)width * height <= INT_MAX) {
982
0
    width *= height;
983
0
    height = 1;
984
0
    src_stride_y = dst_stride_y = 0;
985
0
  }
986
987
0
#if defined(HAS_DIVIDEROW_16_AVX2)
988
0
  if (TestCpuFlag(kCpuHasAVX2)) {
989
0
    DivideRow = DivideRow_16_Any_AVX2;
990
0
    if (IS_ALIGNED(width, 32)) {
991
0
      DivideRow = DivideRow_16_AVX2;
992
0
    }
993
0
  }
994
0
#endif
995
#if defined(HAS_DIVIDEROW_16_NEON)
996
  if (TestCpuFlag(kCpuHasNEON)) {
997
    DivideRow = DivideRow_16_Any_NEON;
998
    if (IS_ALIGNED(width, 16)) {
999
      DivideRow = DivideRow_16_NEON;
1000
    }
1001
  }
1002
#endif
1003
#if defined(HAS_DIVIDEROW_16_SVE2)
1004
  if (TestCpuFlag(kCpuHasSVE2)) {
1005
    DivideRow = DivideRow_16_SVE2;
1006
  }
1007
#endif
1008
1009
0
  for (y = 0; y < height; ++y) {
1010
0
    DivideRow(src_y, dst_y, scale, width);
1011
0
    src_y += src_stride_y;
1012
0
    dst_y += dst_stride_y;
1013
0
  }
1014
0
}
1015
1016
// Swap U and V channels in interleaved UV plane.
1017
LIBYUV_API
1018
void SwapUVPlane(const uint8_t* src_uv,
1019
                 int src_stride_uv,
1020
                 uint8_t* dst_vu,
1021
                 int dst_stride_vu,
1022
                 int width,
1023
0
                 int height) {
1024
0
  int y;
1025
0
  void (*SwapUVRow)(const uint8_t* src_uv, uint8_t* dst_vu, int width) =
1026
0
      SwapUVRow_C;
1027
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1028
0
    return;
1029
0
  }
1030
  // Negative height means invert the image.
1031
0
  if (height < 0) {
1032
0
    height = -height;
1033
0
    src_uv = src_uv + (ptrdiff_t)(height - 1) * src_stride_uv;
1034
0
    src_stride_uv = -src_stride_uv;
1035
0
  }
1036
  // Coalesce rows.
1037
0
  if (src_stride_uv == width * 2 && dst_stride_vu == width * 2 &&
1038
0
      (ptrdiff_t)width * height <= INT_MAX) {
1039
0
    width *= height;
1040
0
    height = 1;
1041
0
    src_stride_uv = dst_stride_vu = 0;
1042
0
  }
1043
1044
0
#if defined(HAS_SWAPUVROW_SSSE3)
1045
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
1046
0
    SwapUVRow = SwapUVRow_Any_SSSE3;
1047
0
    if (IS_ALIGNED(width, 16)) {
1048
0
      SwapUVRow = SwapUVRow_SSSE3;
1049
0
    }
1050
0
  }
1051
0
#endif
1052
0
#if defined(HAS_SWAPUVROW_AVX2)
1053
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1054
0
    SwapUVRow = SwapUVRow_Any_AVX2;
1055
0
    if (IS_ALIGNED(width, 32)) {
1056
0
      SwapUVRow = SwapUVRow_AVX2;
1057
0
    }
1058
0
  }
1059
0
#endif
1060
#if defined(HAS_SWAPUVROW_NEON)
1061
  if (TestCpuFlag(kCpuHasNEON)) {
1062
    SwapUVRow = SwapUVRow_Any_NEON;
1063
    if (IS_ALIGNED(width, 16)) {
1064
      SwapUVRow = SwapUVRow_NEON;
1065
    }
1066
  }
1067
#endif
1068
1069
0
  for (y = 0; y < height; ++y) {
1070
0
    SwapUVRow(src_uv, dst_vu, width);
1071
0
    src_uv += src_stride_uv;
1072
0
    dst_vu += dst_stride_vu;
1073
0
  }
1074
0
}
1075
1076
// Convert NV21 to NV12.
1077
LIBYUV_API
1078
int NV21ToNV12(const uint8_t* src_y,
1079
               int src_stride_y,
1080
               const uint8_t* src_vu,
1081
               int src_stride_vu,
1082
               uint8_t* dst_y,
1083
               int dst_stride_y,
1084
               uint8_t* dst_uv,
1085
               int dst_stride_uv,
1086
               int width,
1087
0
               int height) {
1088
0
  int halfwidth = (width + 1) >> 1;
1089
0
  int halfheight = (height + 1) >> 1;
1090
1091
0
  if (!src_vu || !dst_uv || width <= 0 || height == 0 || height == INT_MIN) {
1092
0
    return -1;
1093
0
  }
1094
1095
0
  if (dst_y) {
1096
0
    CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1097
0
  }
1098
1099
  // Negative height means invert the image.
1100
0
  if (height < 0) {
1101
0
    height = -height;
1102
0
    halfheight = (height + 1) >> 1;
1103
0
    src_vu = src_vu + (ptrdiff_t)(halfheight - 1) * src_stride_vu;
1104
0
    src_stride_vu = -src_stride_vu;
1105
0
  }
1106
1107
0
  SwapUVPlane(src_vu, src_stride_vu, dst_uv, dst_stride_uv, halfwidth,
1108
0
              halfheight);
1109
0
  return 0;
1110
0
}
1111
1112
// Test if tile_height is a power of 2 (16 or 32)
1113
0
#define IS_POWEROFTWO(x) (!((x) & ((x) - 1)))
1114
1115
// Detile a plane of data
1116
// tile width is 16 and assumed.
1117
// tile_height is 16 or 32 for MM21.
1118
// src_stride_y is bytes per row of source ignoring tiling. e.g. 640
1119
// TODO: More detile row functions.
1120
LIBYUV_API
1121
int DetilePlane(const uint8_t* src_y,
1122
                int src_stride_y,
1123
                uint8_t* dst_y,
1124
                int dst_stride_y,
1125
                int width,
1126
                int height,
1127
0
                int tile_height) {
1128
0
  const ptrdiff_t src_tile_stride = 16 * tile_height;
1129
0
  int y;
1130
0
  void (*DetileRow)(const uint8_t* src, ptrdiff_t src_tile_stride, uint8_t* dst,
1131
0
                    int width) = DetileRow_C;
1132
0
  if (!src_y || !dst_y || width <= 0 || height == 0 || height == INT_MIN ||
1133
0
      !IS_POWEROFTWO(tile_height)) {
1134
0
    return -1;
1135
0
  }
1136
1137
  // Negative height means invert the image.
1138
0
  if (height < 0) {
1139
0
    height = -height;
1140
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
1141
0
    dst_stride_y = -dst_stride_y;
1142
0
  }
1143
1144
0
#if defined(HAS_DETILEROW_SSE2)
1145
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1146
0
    DetileRow = DetileRow_Any_SSE2;
1147
0
    if (IS_ALIGNED(width, 16)) {
1148
0
      DetileRow = DetileRow_SSE2;
1149
0
    }
1150
0
  }
1151
0
#endif
1152
#if defined(HAS_DETILEROW_NEON)
1153
  if (TestCpuFlag(kCpuHasNEON)) {
1154
    DetileRow = DetileRow_Any_NEON;
1155
    if (IS_ALIGNED(width, 16)) {
1156
      DetileRow = DetileRow_NEON;
1157
    }
1158
  }
1159
#endif
1160
1161
  // Detile plane
1162
0
  for (y = 0; y < height; ++y) {
1163
0
    DetileRow(src_y, src_tile_stride, dst_y, width);
1164
0
    dst_y += dst_stride_y;
1165
0
    src_y += 16;
1166
    // Advance to next row of tiles.
1167
0
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
1168
0
      src_y = src_y - src_tile_stride + src_stride_y * tile_height;
1169
0
    }
1170
0
  }
1171
0
  return 0;
1172
0
}
1173
1174
// Convert a plane of 16 bit tiles of 16 x H to linear.
1175
// tile width is 16 and assumed.
1176
// tile_height is 16 or 32 for MT2T.
1177
LIBYUV_API
1178
int DetilePlane_16(const uint16_t* src_y,
1179
                   int src_stride_y,
1180
                   uint16_t* dst_y,
1181
                   int dst_stride_y,
1182
                   int width,
1183
                   int height,
1184
0
                   int tile_height) {
1185
0
  const ptrdiff_t src_tile_stride = 16 * tile_height;
1186
0
  int y;
1187
0
  void (*DetileRow_16)(const uint16_t* src, ptrdiff_t src_tile_stride,
1188
0
                       uint16_t* dst, int width) = DetileRow_16_C;
1189
0
  if (!src_y || !dst_y || width <= 0 || height == 0 || height == INT_MIN ||
1190
0
      !IS_POWEROFTWO(tile_height)) {
1191
0
    return -1;
1192
0
  }
1193
1194
  // Negative height means invert the image.
1195
0
  if (height < 0) {
1196
0
    height = -height;
1197
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
1198
0
    dst_stride_y = -dst_stride_y;
1199
0
  }
1200
1201
0
#if defined(HAS_DETILEROW_16_SSE2)
1202
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1203
0
    DetileRow_16 = DetileRow_16_Any_SSE2;
1204
0
    if (IS_ALIGNED(width, 16)) {
1205
0
      DetileRow_16 = DetileRow_16_SSE2;
1206
0
    }
1207
0
  }
1208
0
#endif
1209
0
#if defined(HAS_DETILEROW_16_AVX)
1210
0
  if (TestCpuFlag(kCpuHasAVX)) {
1211
0
    DetileRow_16 = DetileRow_16_Any_AVX;
1212
0
    if (IS_ALIGNED(width, 16)) {
1213
0
      DetileRow_16 = DetileRow_16_AVX;
1214
0
    }
1215
0
  }
1216
0
#endif
1217
#if defined(HAS_DETILEROW_16_NEON)
1218
  if (TestCpuFlag(kCpuHasNEON)) {
1219
    DetileRow_16 = DetileRow_16_Any_NEON;
1220
    if (IS_ALIGNED(width, 16)) {
1221
      DetileRow_16 = DetileRow_16_NEON;
1222
    }
1223
  }
1224
#endif
1225
1226
  // Detile plane
1227
0
  for (y = 0; y < height; ++y) {
1228
0
    DetileRow_16(src_y, src_tile_stride, dst_y, width);
1229
0
    dst_y += dst_stride_y;
1230
0
    src_y += 16;
1231
    // Advance to next row of tiles.
1232
0
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
1233
0
      src_y = src_y - src_tile_stride + src_stride_y * tile_height;
1234
0
    }
1235
0
  }
1236
0
  return 0;
1237
0
}
1238
1239
LIBYUV_API
1240
void DetileSplitUVPlane(const uint8_t* src_uv,
1241
                        int src_stride_uv,
1242
                        uint8_t* dst_u,
1243
                        int dst_stride_u,
1244
                        uint8_t* dst_v,
1245
                        int dst_stride_v,
1246
                        int width,
1247
                        int height,
1248
0
                        int tile_height) {
1249
0
  const ptrdiff_t src_tile_stride = 16 * tile_height;
1250
0
  int y;
1251
0
  void (*DetileSplitUVRow)(const uint8_t* src, ptrdiff_t src_tile_stride,
1252
0
                           uint8_t* dst_u, uint8_t* dst_v, int width) =
1253
0
      DetileSplitUVRow_C;
1254
0
  assert(src_stride_uv >= 0);
1255
0
  assert(tile_height > 0);
1256
0
  assert(src_stride_uv > 0);
1257
1258
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1259
0
    return;
1260
0
  }
1261
  // Negative height means invert the image.
1262
0
  if (height < 0) {
1263
0
    height = -height;
1264
0
    dst_u = dst_u + (ptrdiff_t)(height - 1) * dst_stride_u;
1265
0
    dst_stride_u = -dst_stride_u;
1266
0
    dst_v = dst_v + (ptrdiff_t)(height - 1) * dst_stride_v;
1267
0
    dst_stride_v = -dst_stride_v;
1268
0
  }
1269
1270
0
#if defined(HAS_DETILESPLITUVROW_SSSE3)
1271
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
1272
0
    DetileSplitUVRow = DetileSplitUVRow_Any_SSSE3;
1273
0
    if (IS_ALIGNED(width, 16)) {
1274
0
      DetileSplitUVRow = DetileSplitUVRow_SSSE3;
1275
0
    }
1276
0
  }
1277
0
#endif
1278
#if defined(HAS_DETILESPLITUVROW_NEON)
1279
  if (TestCpuFlag(kCpuHasNEON)) {
1280
    DetileSplitUVRow = DetileSplitUVRow_Any_NEON;
1281
    if (IS_ALIGNED(width, 16)) {
1282
      DetileSplitUVRow = DetileSplitUVRow_NEON;
1283
    }
1284
  }
1285
#endif
1286
1287
  // Detile plane
1288
0
  for (y = 0; y < height; ++y) {
1289
0
    DetileSplitUVRow(src_uv, src_tile_stride, dst_u, dst_v, width);
1290
0
    dst_u += dst_stride_u;
1291
0
    dst_v += dst_stride_v;
1292
0
    src_uv += 16;
1293
    // Advance to next row of tiles.
1294
0
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
1295
0
      src_uv = src_uv - src_tile_stride + src_stride_uv * tile_height;
1296
0
    }
1297
0
  }
1298
0
}
1299
1300
LIBYUV_API
1301
void DetileToYUY2(const uint8_t* src_y,
1302
                  int src_stride_y,
1303
                  const uint8_t* src_uv,
1304
                  int src_stride_uv,
1305
                  uint8_t* dst_yuy2,
1306
                  int dst_stride_yuy2,
1307
                  int width,
1308
                  int height,
1309
0
                  int tile_height) {
1310
0
  const ptrdiff_t src_y_tile_stride = 16 * tile_height;
1311
0
  const ptrdiff_t src_uv_tile_stride = src_y_tile_stride / 2;
1312
0
  int y;
1313
0
  void (*DetileToYUY2)(const uint8_t* src_y, ptrdiff_t src_y_tile_stride,
1314
0
                       const uint8_t* src_uv, ptrdiff_t src_uv_tile_stride,
1315
0
                       uint8_t* dst_yuy2, int width) = DetileToYUY2_C;
1316
0
  assert(src_stride_y >= 0);
1317
0
  assert(src_stride_y > 0);
1318
0
  assert(src_stride_uv >= 0);
1319
0
  assert(src_stride_uv > 0);
1320
0
  assert(tile_height > 0);
1321
1322
0
  if (width <= 0 || height == 0 || height == INT_MIN || tile_height <= 0) {
1323
0
    return;
1324
0
  }
1325
  // Negative height means invert the image.
1326
0
  if (height < 0) {
1327
0
    height = -height;
1328
0
    dst_yuy2 = dst_yuy2 + (ptrdiff_t)(height - 1) * dst_stride_yuy2;
1329
0
    dst_stride_yuy2 = -dst_stride_yuy2;
1330
0
  }
1331
1332
#if defined(HAS_DETILETOYUY2_NEON)
1333
  if (TestCpuFlag(kCpuHasNEON)) {
1334
    DetileToYUY2 = DetileToYUY2_Any_NEON;
1335
    if (IS_ALIGNED(width, 16)) {
1336
      DetileToYUY2 = DetileToYUY2_NEON;
1337
    }
1338
  }
1339
#endif
1340
1341
0
#if defined(HAS_DETILETOYUY2_SSE2)
1342
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1343
0
    DetileToYUY2 = DetileToYUY2_Any_SSE2;
1344
0
    if (IS_ALIGNED(width, 16)) {
1345
0
      DetileToYUY2 = DetileToYUY2_SSE2;
1346
0
    }
1347
0
  }
1348
0
#endif
1349
1350
  // Detile plane
1351
0
  for (y = 0; y < height; ++y) {
1352
0
    DetileToYUY2(src_y, src_y_tile_stride, src_uv, src_uv_tile_stride, dst_yuy2,
1353
0
                 width);
1354
0
    dst_yuy2 += dst_stride_yuy2;
1355
0
    src_y += 16;
1356
1357
0
    if (y & 0x1)
1358
0
      src_uv += 16;
1359
1360
    // Advance to next row of tiles.
1361
0
    if ((y & (tile_height - 1)) == (tile_height - 1)) {
1362
0
      src_y = src_y - src_y_tile_stride + src_stride_y * tile_height;
1363
0
      src_uv = src_uv - src_uv_tile_stride + src_stride_uv * (tile_height / 2);
1364
0
    }
1365
0
  }
1366
0
}
1367
1368
// Support function for NV12 etc RGB channels.
1369
// Width and height are plane sizes (typically half pixel width).
1370
LIBYUV_API
1371
void SplitRGBPlane(const uint8_t* src_rgb,
1372
                   int src_stride_rgb,
1373
                   uint8_t* dst_r,
1374
                   int dst_stride_r,
1375
                   uint8_t* dst_g,
1376
                   int dst_stride_g,
1377
                   uint8_t* dst_b,
1378
                   int dst_stride_b,
1379
                   int width,
1380
0
                   int height) {
1381
0
  int y;
1382
0
  void (*SplitRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
1383
0
                      uint8_t* dst_b, int width) = SplitRGBRow_C;
1384
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1385
0
    return;
1386
0
  }
1387
  // Negative height means invert the image.
1388
0
  if (height < 0) {
1389
0
    height = -height;
1390
0
    dst_r = dst_r + (ptrdiff_t)(height - 1) * dst_stride_r;
1391
0
    dst_g = dst_g + (ptrdiff_t)(height - 1) * dst_stride_g;
1392
0
    dst_b = dst_b + (ptrdiff_t)(height - 1) * dst_stride_b;
1393
0
    dst_stride_r = -dst_stride_r;
1394
0
    dst_stride_g = -dst_stride_g;
1395
0
    dst_stride_b = -dst_stride_b;
1396
0
  }
1397
  // Coalesce rows.
1398
0
  if (src_stride_rgb == width * 3 && dst_stride_r == width &&
1399
0
      dst_stride_g == width && dst_stride_b == width &&
1400
0
      (ptrdiff_t)width * height <= INT_MAX) {
1401
0
    width *= height;
1402
0
    height = 1;
1403
0
    src_stride_rgb = dst_stride_r = dst_stride_g = dst_stride_b = 0;
1404
0
  }
1405
0
#if defined(HAS_SPLITRGBROW_SSSE3)
1406
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
1407
0
    SplitRGBRow = SplitRGBRow_Any_SSSE3;
1408
0
    if (IS_ALIGNED(width, 16)) {
1409
0
      SplitRGBRow = SplitRGBRow_SSSE3;
1410
0
    }
1411
0
  }
1412
0
#endif
1413
0
#if defined(HAS_SPLITRGBROW_SSE41)
1414
0
  if (TestCpuFlag(kCpuHasSSE41)) {
1415
0
    SplitRGBRow = SplitRGBRow_Any_SSE41;
1416
0
    if (IS_ALIGNED(width, 16)) {
1417
0
      SplitRGBRow = SplitRGBRow_SSE41;
1418
0
    }
1419
0
  }
1420
0
#endif
1421
0
#if defined(HAS_SPLITRGBROW_AVX2)
1422
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1423
0
    SplitRGBRow = SplitRGBRow_Any_AVX2;
1424
0
    if (IS_ALIGNED(width, 32)) {
1425
0
      SplitRGBRow = SplitRGBRow_AVX2;
1426
0
    }
1427
0
  }
1428
0
#endif
1429
#if defined(HAS_SPLITRGBROW_NEON)
1430
  if (TestCpuFlag(kCpuHasNEON)) {
1431
    SplitRGBRow = SplitRGBRow_Any_NEON;
1432
    if (IS_ALIGNED(width, 16)) {
1433
      SplitRGBRow = SplitRGBRow_NEON;
1434
    }
1435
  }
1436
#endif
1437
#if defined(HAS_SPLITRGBROW_RVV)
1438
  if (TestCpuFlag(kCpuHasRVV)) {
1439
    SplitRGBRow = SplitRGBRow_RVV;
1440
  }
1441
#endif
1442
1443
0
  for (y = 0; y < height; ++y) {
1444
    // Copy a row of RGB.
1445
0
    SplitRGBRow(src_rgb, dst_r, dst_g, dst_b, width);
1446
0
    dst_r += dst_stride_r;
1447
0
    dst_g += dst_stride_g;
1448
0
    dst_b += dst_stride_b;
1449
0
    src_rgb += src_stride_rgb;
1450
0
  }
1451
0
}
1452
1453
LIBYUV_API
1454
void MergeRGBPlane(const uint8_t* src_r,
1455
                   int src_stride_r,
1456
                   const uint8_t* src_g,
1457
                   int src_stride_g,
1458
                   const uint8_t* src_b,
1459
                   int src_stride_b,
1460
                   uint8_t* dst_rgb,
1461
                   int dst_stride_rgb,
1462
                   int width,
1463
0
                   int height) {
1464
0
  int y;
1465
0
  void (*MergeRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
1466
0
                      const uint8_t* src_b, uint8_t* dst_rgb, int width) =
1467
0
      MergeRGBRow_C;
1468
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1469
0
    return;
1470
0
  }
1471
  // Coalesce rows.
1472
  // Negative height means invert the image.
1473
0
  if (height < 0) {
1474
0
    height = -height;
1475
0
    dst_rgb = dst_rgb + (ptrdiff_t)(height - 1) * dst_stride_rgb;
1476
0
    dst_stride_rgb = -dst_stride_rgb;
1477
0
  }
1478
  // Coalesce rows.
1479
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
1480
0
      dst_stride_rgb == width * 3 && (ptrdiff_t)width * height <= INT_MAX) {
1481
0
    width *= height;
1482
0
    height = 1;
1483
0
    src_stride_r = src_stride_g = src_stride_b = dst_stride_rgb = 0;
1484
0
  }
1485
0
#if defined(HAS_MERGERGBROW_SSSE3)
1486
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
1487
0
    MergeRGBRow = MergeRGBRow_Any_SSSE3;
1488
0
    if (IS_ALIGNED(width, 16)) {
1489
0
      MergeRGBRow = MergeRGBRow_SSSE3;
1490
0
    }
1491
0
  }
1492
0
#endif
1493
#if defined(HAS_MERGERGBROW_NEON)
1494
  if (TestCpuFlag(kCpuHasNEON)) {
1495
    MergeRGBRow = MergeRGBRow_Any_NEON;
1496
    if (IS_ALIGNED(width, 16)) {
1497
      MergeRGBRow = MergeRGBRow_NEON;
1498
    }
1499
  }
1500
#endif
1501
#if defined(HAS_MERGERGBROW_RVV)
1502
  if (TestCpuFlag(kCpuHasRVV)) {
1503
    MergeRGBRow = MergeRGBRow_RVV;
1504
  }
1505
#endif
1506
1507
0
  for (y = 0; y < height; ++y) {
1508
    // Merge a row of U and V into a row of RGB.
1509
0
    MergeRGBRow(src_r, src_g, src_b, dst_rgb, width);
1510
0
    src_r += src_stride_r;
1511
0
    src_g += src_stride_g;
1512
0
    src_b += src_stride_b;
1513
0
    dst_rgb += dst_stride_rgb;
1514
0
  }
1515
0
}
1516
1517
LIBYUV_NOINLINE
1518
static void SplitARGBPlaneAlpha(const uint8_t* src_argb,
1519
                                int src_stride_argb,
1520
                                uint8_t* dst_r,
1521
                                int dst_stride_r,
1522
                                uint8_t* dst_g,
1523
                                int dst_stride_g,
1524
                                uint8_t* dst_b,
1525
                                int dst_stride_b,
1526
                                uint8_t* dst_a,
1527
                                int dst_stride_a,
1528
                                int width,
1529
0
                                int height) {
1530
0
  int y;
1531
0
  void (*SplitARGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
1532
0
                       uint8_t* dst_b, uint8_t* dst_a, int width) =
1533
0
      SplitARGBRow_C;
1534
1535
0
  assert(height >= 0);
1536
1537
0
  if (width <= 0 || height == 0) {
1538
0
    return;
1539
0
  }
1540
0
  if (src_stride_argb == width * 4 && dst_stride_r == width &&
1541
0
      dst_stride_g == width && dst_stride_b == width && dst_stride_a == width &&
1542
0
      (ptrdiff_t)width * height <= INT_MAX) {
1543
0
    width *= height;
1544
0
    height = 1;
1545
0
    src_stride_argb = dst_stride_r = dst_stride_g = dst_stride_b =
1546
0
        dst_stride_a = 0;
1547
0
  }
1548
1549
0
#if defined(HAS_SPLITARGBROW_SSE2)
1550
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1551
0
    SplitARGBRow = SplitARGBRow_Any_SSE2;
1552
0
    if (IS_ALIGNED(width, 8)) {
1553
0
      SplitARGBRow = SplitARGBRow_SSE2;
1554
0
    }
1555
0
  }
1556
0
#endif
1557
0
#if defined(HAS_SPLITARGBROW_SSSE3)
1558
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
1559
0
    SplitARGBRow = SplitARGBRow_Any_SSSE3;
1560
0
    if (IS_ALIGNED(width, 8)) {
1561
0
      SplitARGBRow = SplitARGBRow_SSSE3;
1562
0
    }
1563
0
  }
1564
0
#endif
1565
0
#if defined(HAS_SPLITARGBROW_AVX2)
1566
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1567
0
    SplitARGBRow = SplitARGBRow_Any_AVX2;
1568
0
    if (IS_ALIGNED(width, 16)) {
1569
0
      SplitARGBRow = SplitARGBRow_AVX2;
1570
0
    }
1571
0
  }
1572
0
#endif
1573
#if defined(HAS_SPLITARGBROW_NEON)
1574
  if (TestCpuFlag(kCpuHasNEON)) {
1575
    SplitARGBRow = SplitARGBRow_Any_NEON;
1576
    if (IS_ALIGNED(width, 16)) {
1577
      SplitARGBRow = SplitARGBRow_NEON;
1578
    }
1579
  }
1580
#endif
1581
#if defined(HAS_SPLITARGBROW_RVV)
1582
  if (TestCpuFlag(kCpuHasRVV)) {
1583
    SplitARGBRow = SplitARGBRow_RVV;
1584
  }
1585
#endif
1586
1587
0
  for (y = 0; y < height; ++y) {
1588
0
    SplitARGBRow(src_argb, dst_r, dst_g, dst_b, dst_a, width);
1589
0
    dst_r += dst_stride_r;
1590
0
    dst_g += dst_stride_g;
1591
0
    dst_b += dst_stride_b;
1592
0
    dst_a += dst_stride_a;
1593
0
    src_argb += src_stride_argb;
1594
0
  }
1595
0
}
1596
1597
LIBYUV_NOINLINE
1598
static void SplitARGBPlaneOpaque(const uint8_t* src_argb,
1599
                                 int src_stride_argb,
1600
                                 uint8_t* dst_r,
1601
                                 int dst_stride_r,
1602
                                 uint8_t* dst_g,
1603
                                 int dst_stride_g,
1604
                                 uint8_t* dst_b,
1605
                                 int dst_stride_b,
1606
                                 int width,
1607
0
                                 int height) {
1608
0
  int y;
1609
0
  void (*SplitXRGBRow)(const uint8_t* src_rgb, uint8_t* dst_r, uint8_t* dst_g,
1610
0
                       uint8_t* dst_b, int width) = SplitXRGBRow_C;
1611
0
  assert(height >= 0);
1612
1613
0
  if (width <= 0 || height == 0) {
1614
0
    return;
1615
0
  }
1616
0
  if (src_stride_argb == width * 4 && dst_stride_r == width &&
1617
0
      dst_stride_g == width && dst_stride_b == width &&
1618
0
      (ptrdiff_t)width * height <= INT_MAX) {
1619
0
    width *= height;
1620
0
    height = 1;
1621
0
    src_stride_argb = dst_stride_r = dst_stride_g = dst_stride_b = 0;
1622
0
  }
1623
1624
0
#if defined(HAS_SPLITXRGBROW_SSE2)
1625
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1626
0
    SplitXRGBRow = SplitXRGBRow_Any_SSE2;
1627
0
    if (IS_ALIGNED(width, 8)) {
1628
0
      SplitXRGBRow = SplitXRGBRow_SSE2;
1629
0
    }
1630
0
  }
1631
0
#endif
1632
0
#if defined(HAS_SPLITXRGBROW_SSSE3)
1633
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
1634
0
    SplitXRGBRow = SplitXRGBRow_Any_SSSE3;
1635
0
    if (IS_ALIGNED(width, 8)) {
1636
0
      SplitXRGBRow = SplitXRGBRow_SSSE3;
1637
0
    }
1638
0
  }
1639
0
#endif
1640
0
#if defined(HAS_SPLITXRGBROW_AVX2)
1641
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1642
0
    SplitXRGBRow = SplitXRGBRow_Any_AVX2;
1643
0
    if (IS_ALIGNED(width, 16)) {
1644
0
      SplitXRGBRow = SplitXRGBRow_AVX2;
1645
0
    }
1646
0
  }
1647
0
#endif
1648
#if defined(HAS_SPLITXRGBROW_NEON)
1649
  if (TestCpuFlag(kCpuHasNEON)) {
1650
    SplitXRGBRow = SplitXRGBRow_Any_NEON;
1651
    if (IS_ALIGNED(width, 16)) {
1652
      SplitXRGBRow = SplitXRGBRow_NEON;
1653
    }
1654
  }
1655
#endif
1656
#if defined(HAS_SPLITXRGBROW_RVV)
1657
  if (TestCpuFlag(kCpuHasRVV)) {
1658
    SplitXRGBRow = SplitXRGBRow_RVV;
1659
  }
1660
#endif
1661
1662
0
  for (y = 0; y < height; ++y) {
1663
0
    SplitXRGBRow(src_argb, dst_r, dst_g, dst_b, width);
1664
0
    dst_r += dst_stride_r;
1665
0
    dst_g += dst_stride_g;
1666
0
    dst_b += dst_stride_b;
1667
0
    src_argb += src_stride_argb;
1668
0
  }
1669
0
}
1670
1671
LIBYUV_API
1672
void SplitARGBPlane(const uint8_t* src_argb,
1673
                    int src_stride_argb,
1674
                    uint8_t* dst_r,
1675
                    int dst_stride_r,
1676
                    uint8_t* dst_g,
1677
                    int dst_stride_g,
1678
                    uint8_t* dst_b,
1679
                    int dst_stride_b,
1680
                    uint8_t* dst_a,
1681
                    int dst_stride_a,
1682
                    int width,
1683
0
                    int height) {
1684
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1685
0
    return;
1686
0
  }
1687
  // Negative height means invert the image.
1688
0
  if (height < 0) {
1689
0
    height = -height;
1690
0
    dst_r = dst_r + (ptrdiff_t)(height - 1) * dst_stride_r;
1691
0
    dst_g = dst_g + (ptrdiff_t)(height - 1) * dst_stride_g;
1692
0
    dst_b = dst_b + (ptrdiff_t)(height - 1) * dst_stride_b;
1693
0
    dst_a = dst_a + (ptrdiff_t)(height - 1) * dst_stride_a;
1694
0
    dst_stride_r = -dst_stride_r;
1695
0
    dst_stride_g = -dst_stride_g;
1696
0
    dst_stride_b = -dst_stride_b;
1697
0
    dst_stride_a = -dst_stride_a;
1698
0
  }
1699
1700
0
  if (dst_a == NULL) {
1701
0
    SplitARGBPlaneOpaque(src_argb, src_stride_argb, dst_r, dst_stride_r, dst_g,
1702
0
                         dst_stride_g, dst_b, dst_stride_b, width, height);
1703
0
  } else {
1704
0
    SplitARGBPlaneAlpha(src_argb, src_stride_argb, dst_r, dst_stride_r, dst_g,
1705
0
                        dst_stride_g, dst_b, dst_stride_b, dst_a, dst_stride_a,
1706
0
                        width, height);
1707
0
  }
1708
0
}
1709
1710
LIBYUV_NOINLINE
1711
static void MergeARGBPlaneAlpha(const uint8_t* src_r,
1712
                                int src_stride_r,
1713
                                const uint8_t* src_g,
1714
                                int src_stride_g,
1715
                                const uint8_t* src_b,
1716
                                int src_stride_b,
1717
                                const uint8_t* src_a,
1718
                                int src_stride_a,
1719
                                uint8_t* dst_argb,
1720
                                int dst_stride_argb,
1721
                                int width,
1722
0
                                int height) {
1723
0
  int y;
1724
0
  void (*MergeARGBRow)(const uint8_t* src_r, const uint8_t* src_g,
1725
0
                       const uint8_t* src_b, const uint8_t* src_a,
1726
0
                       uint8_t* dst_argb, int width) = MergeARGBRow_C;
1727
1728
0
  assert(height >= 0);
1729
1730
0
  if (width <= 0 || height == 0) {
1731
0
    return;
1732
0
  }
1733
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
1734
0
      src_stride_a == width && dst_stride_argb == width * 4 &&
1735
0
      (ptrdiff_t)width * height <= INT_MAX) {
1736
0
    width *= height;
1737
0
    height = 1;
1738
0
    src_stride_r = src_stride_g = src_stride_b = src_stride_a =
1739
0
        dst_stride_argb = 0;
1740
0
  }
1741
0
#if defined(HAS_MERGEARGBROW_SSE2)
1742
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1743
0
    MergeARGBRow = MergeARGBRow_Any_SSE2;
1744
0
    if (IS_ALIGNED(width, 8)) {
1745
0
      MergeARGBRow = MergeARGBRow_SSE2;
1746
0
    }
1747
0
  }
1748
0
#endif
1749
0
#if defined(HAS_MERGEARGBROW_AVX2)
1750
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1751
0
    MergeARGBRow = MergeARGBRow_Any_AVX2;
1752
0
    if (IS_ALIGNED(width, 16)) {
1753
0
      MergeARGBRow = MergeARGBRow_AVX2;
1754
0
    }
1755
0
  }
1756
0
#endif
1757
#if defined(HAS_MERGEARGBROW_NEON)
1758
  if (TestCpuFlag(kCpuHasNEON)) {
1759
    MergeARGBRow = MergeARGBRow_Any_NEON;
1760
    if (IS_ALIGNED(width, 16)) {
1761
      MergeARGBRow = MergeARGBRow_NEON;
1762
    }
1763
  }
1764
#endif
1765
#if defined(HAS_MERGEARGBROW_RVV)
1766
  if (TestCpuFlag(kCpuHasRVV)) {
1767
    MergeARGBRow = MergeARGBRow_RVV;
1768
  }
1769
#endif
1770
1771
0
  for (y = 0; y < height; ++y) {
1772
0
    MergeARGBRow(src_r, src_g, src_b, src_a, dst_argb, width);
1773
0
    src_r += src_stride_r;
1774
0
    src_g += src_stride_g;
1775
0
    src_b += src_stride_b;
1776
0
    src_a += src_stride_a;
1777
0
    dst_argb += dst_stride_argb;
1778
0
  }
1779
0
}
1780
1781
LIBYUV_NOINLINE
1782
static void MergeARGBPlaneOpaque(const uint8_t* src_r,
1783
                                 int src_stride_r,
1784
                                 const uint8_t* src_g,
1785
                                 int src_stride_g,
1786
                                 const uint8_t* src_b,
1787
                                 int src_stride_b,
1788
                                 uint8_t* dst_argb,
1789
                                 int dst_stride_argb,
1790
                                 int width,
1791
0
                                 int height) {
1792
0
  int y;
1793
0
  void (*MergeXRGBRow)(const uint8_t* src_r, const uint8_t* src_g,
1794
0
                       const uint8_t* src_b, uint8_t* dst_argb, int width) =
1795
0
      MergeXRGBRow_C;
1796
1797
0
  assert(height >= 0);
1798
1799
0
  if (width <= 0 || height == 0) {
1800
0
    return;
1801
0
  }
1802
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
1803
0
      dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
1804
0
    width *= height;
1805
0
    height = 1;
1806
0
    src_stride_r = src_stride_g = src_stride_b = dst_stride_argb = 0;
1807
0
  }
1808
0
#if defined(HAS_MERGEXRGBROW_SSE2)
1809
0
  if (TestCpuFlag(kCpuHasSSE2)) {
1810
0
    MergeXRGBRow = MergeXRGBRow_Any_SSE2;
1811
0
    if (IS_ALIGNED(width, 8)) {
1812
0
      MergeXRGBRow = MergeXRGBRow_SSE2;
1813
0
    }
1814
0
  }
1815
0
#endif
1816
0
#if defined(HAS_MERGEXRGBROW_AVX2)
1817
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1818
0
    MergeXRGBRow = MergeXRGBRow_Any_AVX2;
1819
0
    if (IS_ALIGNED(width, 16)) {
1820
0
      MergeXRGBRow = MergeXRGBRow_AVX2;
1821
0
    }
1822
0
  }
1823
0
#endif
1824
#if defined(HAS_MERGEXRGBROW_NEON)
1825
  if (TestCpuFlag(kCpuHasNEON)) {
1826
    MergeXRGBRow = MergeXRGBRow_Any_NEON;
1827
    if (IS_ALIGNED(width, 16)) {
1828
      MergeXRGBRow = MergeXRGBRow_NEON;
1829
    }
1830
  }
1831
#endif
1832
#if defined(HAS_MERGEXRGBROW_RVV)
1833
  if (TestCpuFlag(kCpuHasRVV)) {
1834
    MergeXRGBRow = MergeXRGBRow_RVV;
1835
  }
1836
#endif
1837
1838
0
  for (y = 0; y < height; ++y) {
1839
0
    MergeXRGBRow(src_r, src_g, src_b, dst_argb, width);
1840
0
    src_r += src_stride_r;
1841
0
    src_g += src_stride_g;
1842
0
    src_b += src_stride_b;
1843
0
    dst_argb += dst_stride_argb;
1844
0
  }
1845
0
}
1846
1847
LIBYUV_API
1848
void MergeARGBPlane(const uint8_t* src_r,
1849
                    int src_stride_r,
1850
                    const uint8_t* src_g,
1851
                    int src_stride_g,
1852
                    const uint8_t* src_b,
1853
                    int src_stride_b,
1854
                    const uint8_t* src_a,
1855
                    int src_stride_a,
1856
                    uint8_t* dst_argb,
1857
                    int dst_stride_argb,
1858
                    int width,
1859
0
                    int height) {
1860
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1861
0
    return;
1862
0
  }
1863
  // Negative height means invert the image.
1864
0
  if (height < 0) {
1865
0
    height = -height;
1866
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
1867
0
    dst_stride_argb = -dst_stride_argb;
1868
0
  }
1869
1870
0
  if (src_a == NULL) {
1871
0
    MergeARGBPlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
1872
0
                         src_stride_b, dst_argb, dst_stride_argb, width,
1873
0
                         height);
1874
0
  } else {
1875
0
    MergeARGBPlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
1876
0
                        src_stride_b, src_a, src_stride_a, dst_argb,
1877
0
                        dst_stride_argb, width, height);
1878
0
  }
1879
0
}
1880
1881
// TODO(yuan): Support 2 bit alpha channel.
1882
LIBYUV_API
1883
void MergeXR30Plane(const uint16_t* src_r,
1884
                    int src_stride_r,
1885
                    const uint16_t* src_g,
1886
                    int src_stride_g,
1887
                    const uint16_t* src_b,
1888
                    int src_stride_b,
1889
                    uint8_t* dst_ar30,
1890
                    int dst_stride_ar30,
1891
                    int width,
1892
                    int height,
1893
0
                    int depth) {
1894
0
  int y;
1895
0
  void (*MergeXR30Row)(const uint16_t* src_r, const uint16_t* src_g,
1896
0
                       const uint16_t* src_b, uint8_t* dst_ar30, int depth,
1897
0
                       int width) = MergeXR30Row_C;
1898
1899
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
1900
0
    return;
1901
0
  }
1902
  // Negative height means invert the image.
1903
0
  if (height < 0) {
1904
0
    height = -height;
1905
0
    dst_ar30 = dst_ar30 + (ptrdiff_t)(height - 1) * dst_stride_ar30;
1906
0
    dst_stride_ar30 = -dst_stride_ar30;
1907
0
  }
1908
  // Coalesce rows.
1909
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
1910
0
      dst_stride_ar30 == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
1911
0
    width *= height;
1912
0
    height = 1;
1913
0
    src_stride_r = src_stride_g = src_stride_b = dst_stride_ar30 = 0;
1914
0
  }
1915
0
#if defined(HAS_MERGEXR30ROW_AVX2)
1916
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1917
0
    MergeXR30Row = MergeXR30Row_Any_AVX2;
1918
0
    if (IS_ALIGNED(width, 16)) {
1919
0
      MergeXR30Row = MergeXR30Row_AVX2;
1920
0
    }
1921
0
  }
1922
0
#endif
1923
#if defined(HAS_MERGEXR30ROW_NEON)
1924
  if (TestCpuFlag(kCpuHasNEON)) {
1925
    if (depth == 10) {
1926
      MergeXR30Row = MergeXR30Row_10_Any_NEON;
1927
      if (IS_ALIGNED(width, 8)) {
1928
        MergeXR30Row = MergeXR30Row_10_NEON;
1929
      }
1930
    } else {
1931
      MergeXR30Row = MergeXR30Row_Any_NEON;
1932
      if (IS_ALIGNED(width, 8)) {
1933
        MergeXR30Row = MergeXR30Row_NEON;
1934
      }
1935
    }
1936
  }
1937
#endif
1938
1939
0
  for (y = 0; y < height; ++y) {
1940
0
    MergeXR30Row(src_r, src_g, src_b, dst_ar30, depth, width);
1941
0
    src_r += src_stride_r;
1942
0
    src_g += src_stride_g;
1943
0
    src_b += src_stride_b;
1944
0
    dst_ar30 += dst_stride_ar30;
1945
0
  }
1946
0
}
1947
1948
LIBYUV_NOINLINE
1949
static void MergeAR64PlaneAlpha(const uint16_t* src_r,
1950
                                int src_stride_r,
1951
                                const uint16_t* src_g,
1952
                                int src_stride_g,
1953
                                const uint16_t* src_b,
1954
                                int src_stride_b,
1955
                                const uint16_t* src_a,
1956
                                int src_stride_a,
1957
                                uint16_t* dst_ar64,
1958
                                int dst_stride_ar64,
1959
                                int width,
1960
                                int height,
1961
0
                                int depth) {
1962
0
  int y;
1963
0
  void (*MergeAR64Row)(const uint16_t* src_r, const uint16_t* src_g,
1964
0
                       const uint16_t* src_b, const uint16_t* src_a,
1965
0
                       uint16_t* dst_argb, int depth, int width) =
1966
0
      MergeAR64Row_C;
1967
1968
0
  assert(height >= 0);
1969
1970
0
  if (width <= 0 || height == 0) {
1971
0
    return;
1972
0
  }
1973
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
1974
0
      src_stride_a == width && dst_stride_ar64 == width * 4 &&
1975
0
      (ptrdiff_t)width * height <= INT_MAX) {
1976
0
    width *= height;
1977
0
    height = 1;
1978
0
    src_stride_r = src_stride_g = src_stride_b = src_stride_a =
1979
0
        dst_stride_ar64 = 0;
1980
0
  }
1981
0
#if defined(HAS_MERGEAR64ROW_AVX2)
1982
0
  if (TestCpuFlag(kCpuHasAVX2)) {
1983
0
    MergeAR64Row = MergeAR64Row_Any_AVX2;
1984
0
    if (IS_ALIGNED(width, 16)) {
1985
0
      MergeAR64Row = MergeAR64Row_AVX2;
1986
0
    }
1987
0
  }
1988
0
#endif
1989
#if defined(HAS_MERGEAR64ROW_NEON)
1990
  if (TestCpuFlag(kCpuHasNEON)) {
1991
    MergeAR64Row = MergeAR64Row_Any_NEON;
1992
    if (IS_ALIGNED(width, 8)) {
1993
      MergeAR64Row = MergeAR64Row_NEON;
1994
    }
1995
  }
1996
#endif
1997
1998
0
  for (y = 0; y < height; ++y) {
1999
0
    MergeAR64Row(src_r, src_g, src_b, src_a, dst_ar64, depth, width);
2000
0
    src_r += src_stride_r;
2001
0
    src_g += src_stride_g;
2002
0
    src_b += src_stride_b;
2003
0
    src_a += src_stride_a;
2004
0
    dst_ar64 += dst_stride_ar64;
2005
0
  }
2006
0
}
2007
2008
LIBYUV_NOINLINE
2009
static void MergeAR64PlaneOpaque(const uint16_t* src_r,
2010
                                 int src_stride_r,
2011
                                 const uint16_t* src_g,
2012
                                 int src_stride_g,
2013
                                 const uint16_t* src_b,
2014
                                 int src_stride_b,
2015
                                 uint16_t* dst_ar64,
2016
                                 int dst_stride_ar64,
2017
                                 int width,
2018
                                 int height,
2019
0
                                 int depth) {
2020
0
  int y;
2021
0
  void (*MergeXR64Row)(const uint16_t* src_r, const uint16_t* src_g,
2022
0
                       const uint16_t* src_b, uint16_t* dst_argb, int depth,
2023
0
                       int width) = MergeXR64Row_C;
2024
2025
0
  assert(height >= 0);
2026
2027
0
  if (width <= 0 || height == 0) {
2028
0
    return;
2029
0
  }
2030
  // Coalesce rows.
2031
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
2032
0
      dst_stride_ar64 == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
2033
0
    width *= height;
2034
0
    height = 1;
2035
0
    src_stride_r = src_stride_g = src_stride_b = dst_stride_ar64 = 0;
2036
0
  }
2037
0
#if defined(HAS_MERGEXR64ROW_AVX2)
2038
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2039
0
    MergeXR64Row = MergeXR64Row_Any_AVX2;
2040
0
    if (IS_ALIGNED(width, 16)) {
2041
0
      MergeXR64Row = MergeXR64Row_AVX2;
2042
0
    }
2043
0
  }
2044
0
#endif
2045
#if defined(HAS_MERGEXR64ROW_NEON)
2046
  if (TestCpuFlag(kCpuHasNEON)) {
2047
    MergeXR64Row = MergeXR64Row_Any_NEON;
2048
    if (IS_ALIGNED(width, 8)) {
2049
      MergeXR64Row = MergeXR64Row_NEON;
2050
    }
2051
  }
2052
#endif
2053
2054
0
  for (y = 0; y < height; ++y) {
2055
0
    MergeXR64Row(src_r, src_g, src_b, dst_ar64, depth, width);
2056
0
    src_r += src_stride_r;
2057
0
    src_g += src_stride_g;
2058
0
    src_b += src_stride_b;
2059
0
    dst_ar64 += dst_stride_ar64;
2060
0
  }
2061
0
}
2062
2063
LIBYUV_API
2064
void MergeAR64Plane(const uint16_t* src_r,
2065
                    int src_stride_r,
2066
                    const uint16_t* src_g,
2067
                    int src_stride_g,
2068
                    const uint16_t* src_b,
2069
                    int src_stride_b,
2070
                    const uint16_t* src_a,
2071
                    int src_stride_a,
2072
                    uint16_t* dst_ar64,
2073
                    int dst_stride_ar64,
2074
                    int width,
2075
                    int height,
2076
0
                    int depth) {
2077
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
2078
0
    return;
2079
0
  }
2080
  // Negative height means invert the image.
2081
0
  if (height < 0) {
2082
0
    height = -height;
2083
0
    dst_ar64 = dst_ar64 + (ptrdiff_t)(height - 1) * dst_stride_ar64;
2084
0
    dst_stride_ar64 = -dst_stride_ar64;
2085
0
  }
2086
2087
0
  if (src_a == NULL) {
2088
0
    MergeAR64PlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
2089
0
                         src_stride_b, dst_ar64, dst_stride_ar64, width, height,
2090
0
                         depth);
2091
0
  } else {
2092
0
    MergeAR64PlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
2093
0
                        src_stride_b, src_a, src_stride_a, dst_ar64,
2094
0
                        dst_stride_ar64, width, height, depth);
2095
0
  }
2096
0
}
2097
2098
LIBYUV_NOINLINE
2099
static void MergeARGB16To8PlaneAlpha(const uint16_t* src_r,
2100
                                     int src_stride_r,
2101
                                     const uint16_t* src_g,
2102
                                     int src_stride_g,
2103
                                     const uint16_t* src_b,
2104
                                     int src_stride_b,
2105
                                     const uint16_t* src_a,
2106
                                     int src_stride_a,
2107
                                     uint8_t* dst_argb,
2108
                                     int dst_stride_argb,
2109
                                     int width,
2110
                                     int height,
2111
0
                                     int depth) {
2112
0
  int y;
2113
0
  void (*MergeARGB16To8Row)(const uint16_t* src_r, const uint16_t* src_g,
2114
0
                            const uint16_t* src_b, const uint16_t* src_a,
2115
0
                            uint8_t* dst_argb, int depth, int width) =
2116
0
      MergeARGB16To8Row_C;
2117
2118
0
  assert(height >= 0);
2119
2120
0
  if (width <= 0 || height == 0) {
2121
0
    return;
2122
0
  }
2123
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
2124
0
      src_stride_a == width && dst_stride_argb == width * 4 &&
2125
0
      (ptrdiff_t)width * height <= INT_MAX) {
2126
0
    width *= height;
2127
0
    height = 1;
2128
0
    src_stride_r = src_stride_g = src_stride_b = src_stride_a =
2129
0
        dst_stride_argb = 0;
2130
0
  }
2131
0
#if defined(HAS_MERGEARGB16TO8ROW_AVX2)
2132
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2133
0
    MergeARGB16To8Row = MergeARGB16To8Row_Any_AVX2;
2134
0
    if (IS_ALIGNED(width, 16)) {
2135
0
      MergeARGB16To8Row = MergeARGB16To8Row_AVX2;
2136
0
    }
2137
0
  }
2138
0
#endif
2139
#if defined(HAS_MERGEARGB16TO8ROW_NEON)
2140
  if (TestCpuFlag(kCpuHasNEON)) {
2141
    MergeARGB16To8Row = MergeARGB16To8Row_Any_NEON;
2142
    if (IS_ALIGNED(width, 8)) {
2143
      MergeARGB16To8Row = MergeARGB16To8Row_NEON;
2144
    }
2145
  }
2146
#endif
2147
2148
0
  for (y = 0; y < height; ++y) {
2149
0
    MergeARGB16To8Row(src_r, src_g, src_b, src_a, dst_argb, depth, width);
2150
0
    src_r += src_stride_r;
2151
0
    src_g += src_stride_g;
2152
0
    src_b += src_stride_b;
2153
0
    src_a += src_stride_a;
2154
0
    dst_argb += dst_stride_argb;
2155
0
  }
2156
0
}
2157
2158
LIBYUV_NOINLINE
2159
static void MergeARGB16To8PlaneOpaque(const uint16_t* src_r,
2160
                                      int src_stride_r,
2161
                                      const uint16_t* src_g,
2162
                                      int src_stride_g,
2163
                                      const uint16_t* src_b,
2164
                                      int src_stride_b,
2165
                                      uint8_t* dst_argb,
2166
                                      int dst_stride_argb,
2167
                                      int width,
2168
                                      int height,
2169
0
                                      int depth) {
2170
0
  int y;
2171
0
  void (*MergeXRGB16To8Row)(const uint16_t* src_r, const uint16_t* src_g,
2172
0
                            const uint16_t* src_b, uint8_t* dst_argb, int depth,
2173
0
                            int width) = MergeXRGB16To8Row_C;
2174
2175
0
  assert(height >= 0);
2176
2177
0
  if (width <= 0 || height == 0) {
2178
0
    return;
2179
0
  }
2180
  // Coalesce rows.
2181
0
  if (src_stride_r == width && src_stride_g == width && src_stride_b == width &&
2182
0
      dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
2183
0
    width *= height;
2184
0
    height = 1;
2185
0
    src_stride_r = src_stride_g = src_stride_b = dst_stride_argb = 0;
2186
0
  }
2187
0
#if defined(HAS_MERGEXRGB16TO8ROW_AVX2)
2188
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2189
0
    MergeXRGB16To8Row = MergeXRGB16To8Row_Any_AVX2;
2190
0
    if (IS_ALIGNED(width, 16)) {
2191
0
      MergeXRGB16To8Row = MergeXRGB16To8Row_AVX2;
2192
0
    }
2193
0
  }
2194
0
#endif
2195
#if defined(HAS_MERGEXRGB16TO8ROW_NEON)
2196
  if (TestCpuFlag(kCpuHasNEON)) {
2197
    MergeXRGB16To8Row = MergeXRGB16To8Row_Any_NEON;
2198
    if (IS_ALIGNED(width, 8)) {
2199
      MergeXRGB16To8Row = MergeXRGB16To8Row_NEON;
2200
    }
2201
  }
2202
#endif
2203
2204
0
  for (y = 0; y < height; ++y) {
2205
0
    MergeXRGB16To8Row(src_r, src_g, src_b, dst_argb, depth, width);
2206
0
    src_r += src_stride_r;
2207
0
    src_g += src_stride_g;
2208
0
    src_b += src_stride_b;
2209
0
    dst_argb += dst_stride_argb;
2210
0
  }
2211
0
}
2212
2213
LIBYUV_API
2214
void MergeARGB16To8Plane(const uint16_t* src_r,
2215
                         int src_stride_r,
2216
                         const uint16_t* src_g,
2217
                         int src_stride_g,
2218
                         const uint16_t* src_b,
2219
                         int src_stride_b,
2220
                         const uint16_t* src_a,
2221
                         int src_stride_a,
2222
                         uint8_t* dst_argb,
2223
                         int dst_stride_argb,
2224
                         int width,
2225
                         int height,
2226
0
                         int depth) {
2227
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
2228
0
    return;
2229
0
  }
2230
  // Negative height means invert the image.
2231
0
  if (height < 0) {
2232
0
    height = -height;
2233
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
2234
0
    dst_stride_argb = -dst_stride_argb;
2235
0
  }
2236
2237
0
  if (src_a == NULL) {
2238
0
    MergeARGB16To8PlaneOpaque(src_r, src_stride_r, src_g, src_stride_g, src_b,
2239
0
                              src_stride_b, dst_argb, dst_stride_argb, width,
2240
0
                              height, depth);
2241
0
  } else {
2242
0
    MergeARGB16To8PlaneAlpha(src_r, src_stride_r, src_g, src_stride_g, src_b,
2243
0
                             src_stride_b, src_a, src_stride_a, dst_argb,
2244
0
                             dst_stride_argb, width, height, depth);
2245
0
  }
2246
0
}
2247
2248
// Convert YUY2 to I422.
2249
LIBYUV_API
2250
int YUY2ToI422(const uint8_t* src_yuy2,
2251
               int src_stride_yuy2,
2252
               uint8_t* dst_y,
2253
               int dst_stride_y,
2254
               uint8_t* dst_u,
2255
               int dst_stride_u,
2256
               uint8_t* dst_v,
2257
               int dst_stride_v,
2258
               int width,
2259
0
               int height) {
2260
0
  int y;
2261
0
  void (*YUY2ToUV422Row)(const uint8_t* src_yuy2, uint8_t* dst_u,
2262
0
                         uint8_t* dst_v, int width) = YUY2ToUV422Row_C;
2263
0
  void (*YUY2ToYRow)(const uint8_t* src_yuy2, uint8_t* dst_y, int width) =
2264
0
      YUY2ToYRow_C;
2265
0
  if (!src_yuy2 || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0 ||
2266
0
      height == INT_MIN) {
2267
0
    return -1;
2268
0
  }
2269
  // Negative height means invert the image.
2270
0
  if (height < 0) {
2271
0
    height = -height;
2272
0
    src_yuy2 = src_yuy2 + (ptrdiff_t)(height - 1) * src_stride_yuy2;
2273
0
    src_stride_yuy2 = -src_stride_yuy2;
2274
0
  }
2275
  // Coalesce rows.
2276
0
  if (src_stride_yuy2 == width * 2 && dst_stride_y == width &&
2277
0
      dst_stride_u * 2 == width && dst_stride_v * 2 == width &&
2278
0
      (ptrdiff_t)width * height <= 32768) {
2279
0
    width *= height;
2280
0
    height = 1;
2281
0
    src_stride_yuy2 = dst_stride_y = dst_stride_u = dst_stride_v = 0;
2282
0
  }
2283
0
#if defined(HAS_YUY2TOYROW_SSE2)
2284
0
  if (TestCpuFlag(kCpuHasSSE2)) {
2285
0
    YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
2286
0
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
2287
0
    if (IS_ALIGNED(width, 16)) {
2288
0
      YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
2289
0
      YUY2ToYRow = YUY2ToYRow_SSE2;
2290
0
    }
2291
0
  }
2292
0
#endif
2293
0
#if defined(HAS_YUY2TOYROW_AVX2)
2294
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2295
0
    YUY2ToUV422Row = YUY2ToUV422Row_Any_AVX2;
2296
0
    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
2297
0
    if (IS_ALIGNED(width, 32)) {
2298
0
      YUY2ToUV422Row = YUY2ToUV422Row_AVX2;
2299
0
      YUY2ToYRow = YUY2ToYRow_AVX2;
2300
0
    }
2301
0
  }
2302
0
#endif
2303
#if defined(HAS_YUY2TOYROW_NEON)
2304
  if (TestCpuFlag(kCpuHasNEON)) {
2305
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
2306
    YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
2307
    if (IS_ALIGNED(width, 16)) {
2308
      YUY2ToYRow = YUY2ToYRow_NEON;
2309
      YUY2ToUV422Row = YUY2ToUV422Row_NEON;
2310
    }
2311
  }
2312
#endif
2313
#if defined(HAS_YUY2TOYROW_SVE2)
2314
  if (TestCpuFlag(kCpuHasSVE2)) {
2315
    YUY2ToYRow = YUY2ToYRow_SVE2;
2316
  }
2317
#endif
2318
#if defined(HAS_YUY2TOYROW_LSX) && defined(HAS_YUY2TOUV422ROW_LSX)
2319
  if (TestCpuFlag(kCpuHasLSX)) {
2320
    YUY2ToYRow = YUY2ToYRow_Any_LSX;
2321
    YUY2ToUV422Row = YUY2ToUV422Row_Any_LSX;
2322
    if (IS_ALIGNED(width, 16)) {
2323
      YUY2ToYRow = YUY2ToYRow_LSX;
2324
      YUY2ToUV422Row = YUY2ToUV422Row_LSX;
2325
    }
2326
  }
2327
#endif
2328
#if defined(HAS_YUY2TOYROW_LASX) && defined(HAS_YUY2TOUV422ROW_LASX)
2329
  if (TestCpuFlag(kCpuHasLASX)) {
2330
    YUY2ToYRow = YUY2ToYRow_Any_LASX;
2331
    YUY2ToUV422Row = YUY2ToUV422Row_Any_LASX;
2332
    if (IS_ALIGNED(width, 32)) {
2333
      YUY2ToYRow = YUY2ToYRow_LASX;
2334
      YUY2ToUV422Row = YUY2ToUV422Row_LASX;
2335
    }
2336
  }
2337
#endif
2338
2339
0
  for (y = 0; y < height; ++y) {
2340
0
    YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
2341
0
    YUY2ToYRow(src_yuy2, dst_y, width);
2342
0
    src_yuy2 += src_stride_yuy2;
2343
0
    dst_y += dst_stride_y;
2344
0
    dst_u += dst_stride_u;
2345
0
    dst_v += dst_stride_v;
2346
0
  }
2347
0
  return 0;
2348
0
}
2349
2350
// Convert UYVY to I422.
2351
LIBYUV_API
2352
int UYVYToI422(const uint8_t* src_uyvy,
2353
               int src_stride_uyvy,
2354
               uint8_t* dst_y,
2355
               int dst_stride_y,
2356
               uint8_t* dst_u,
2357
               int dst_stride_u,
2358
               uint8_t* dst_v,
2359
               int dst_stride_v,
2360
               int width,
2361
0
               int height) {
2362
0
  int y;
2363
0
  void (*UYVYToUV422Row)(const uint8_t* src_uyvy, uint8_t* dst_u,
2364
0
                         uint8_t* dst_v, int width) = UYVYToUV422Row_C;
2365
0
  void (*UYVYToYRow)(const uint8_t* src_uyvy, uint8_t* dst_y, int width) =
2366
0
      UYVYToYRow_C;
2367
0
  if (!src_uyvy || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0 ||
2368
0
      height == INT_MIN) {
2369
0
    return -1;
2370
0
  }
2371
  // Negative height means invert the image.
2372
0
  if (height < 0) {
2373
0
    height = -height;
2374
0
    src_uyvy = src_uyvy + (ptrdiff_t)(height - 1) * src_stride_uyvy;
2375
0
    src_stride_uyvy = -src_stride_uyvy;
2376
0
  }
2377
  // Coalesce rows.
2378
0
  if (src_stride_uyvy == width * 2 && dst_stride_y == width &&
2379
0
      dst_stride_u * 2 == width && dst_stride_v * 2 == width &&
2380
0
      (ptrdiff_t)width * height <= 32768) {
2381
0
    width *= height;
2382
0
    height = 1;
2383
0
    src_stride_uyvy = dst_stride_y = dst_stride_u = dst_stride_v = 0;
2384
0
  }
2385
0
#if defined(HAS_UYVYTOYROW_SSE2)
2386
0
  if (TestCpuFlag(kCpuHasSSE2)) {
2387
0
    UYVYToUV422Row = UYVYToUV422Row_Any_SSE2;
2388
0
    UYVYToYRow = UYVYToYRow_Any_SSE2;
2389
0
    if (IS_ALIGNED(width, 16)) {
2390
0
      UYVYToUV422Row = UYVYToUV422Row_SSE2;
2391
0
      UYVYToYRow = UYVYToYRow_SSE2;
2392
0
    }
2393
0
  }
2394
0
#endif
2395
0
#if defined(HAS_UYVYTOYROW_AVX2)
2396
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2397
0
    UYVYToUV422Row = UYVYToUV422Row_Any_AVX2;
2398
0
    UYVYToYRow = UYVYToYRow_Any_AVX2;
2399
0
    if (IS_ALIGNED(width, 32)) {
2400
0
      UYVYToUV422Row = UYVYToUV422Row_AVX2;
2401
0
      UYVYToYRow = UYVYToYRow_AVX2;
2402
0
    }
2403
0
  }
2404
0
#endif
2405
#if defined(HAS_UYVYTOYROW_NEON)
2406
  if (TestCpuFlag(kCpuHasNEON)) {
2407
    UYVYToYRow = UYVYToYRow_Any_NEON;
2408
    UYVYToUV422Row = UYVYToUV422Row_Any_NEON;
2409
    if (IS_ALIGNED(width, 16)) {
2410
      UYVYToYRow = UYVYToYRow_NEON;
2411
      UYVYToUV422Row = UYVYToUV422Row_NEON;
2412
    }
2413
  }
2414
#endif
2415
#if defined(HAS_UYVYTOYROW_SVE2)
2416
  if (TestCpuFlag(kCpuHasSVE2)) {
2417
    UYVYToYRow = UYVYToYRow_SVE2;
2418
  }
2419
#endif
2420
#if defined(HAS_UYVYTOYROW_LSX) && defined(HAS_UYVYTOUV422ROW_LSX)
2421
  if (TestCpuFlag(kCpuHasLSX)) {
2422
    UYVYToYRow = UYVYToYRow_Any_LSX;
2423
    UYVYToUV422Row = UYVYToUV422Row_Any_LSX;
2424
    if (IS_ALIGNED(width, 16)) {
2425
      UYVYToYRow = UYVYToYRow_LSX;
2426
      UYVYToUV422Row = UYVYToUV422Row_LSX;
2427
    }
2428
  }
2429
#endif
2430
#if defined(HAS_UYVYTOYROW_LASX) && defined(HAS_UYVYTOUV422ROW_LASX)
2431
  if (TestCpuFlag(kCpuHasLASX)) {
2432
    UYVYToYRow = UYVYToYRow_Any_LASX;
2433
    UYVYToUV422Row = UYVYToUV422Row_Any_LASX;
2434
    if (IS_ALIGNED(width, 32)) {
2435
      UYVYToYRow = UYVYToYRow_LASX;
2436
      UYVYToUV422Row = UYVYToUV422Row_LASX;
2437
    }
2438
  }
2439
#endif
2440
2441
0
  for (y = 0; y < height; ++y) {
2442
0
    UYVYToUV422Row(src_uyvy, dst_u, dst_v, width);
2443
0
    UYVYToYRow(src_uyvy, dst_y, width);
2444
0
    src_uyvy += src_stride_uyvy;
2445
0
    dst_y += dst_stride_y;
2446
0
    dst_u += dst_stride_u;
2447
0
    dst_v += dst_stride_v;
2448
0
  }
2449
0
  return 0;
2450
0
}
2451
2452
// Convert YUY2 to Y.
2453
LIBYUV_API
2454
int YUY2ToY(const uint8_t* src_yuy2,
2455
            int src_stride_yuy2,
2456
            uint8_t* dst_y,
2457
            int dst_stride_y,
2458
            int width,
2459
0
            int height) {
2460
0
  int y;
2461
0
  void (*YUY2ToYRow)(const uint8_t* src_yuy2, uint8_t* dst_y, int width) =
2462
0
      YUY2ToYRow_C;
2463
0
  if (!src_yuy2 || !dst_y || width <= 0 || height == 0 || height == INT_MIN) {
2464
0
    return -1;
2465
0
  }
2466
  // Negative height means invert the image.
2467
0
  if (height < 0) {
2468
0
    height = -height;
2469
0
    src_yuy2 = src_yuy2 + (ptrdiff_t)(height - 1) * src_stride_yuy2;
2470
0
    src_stride_yuy2 = -src_stride_yuy2;
2471
0
  }
2472
  // Coalesce rows.
2473
0
  if (src_stride_yuy2 == width * 2 && dst_stride_y == width &&
2474
0
      (ptrdiff_t)width * height <= INT_MAX) {
2475
0
    width *= height;
2476
0
    height = 1;
2477
0
    src_stride_yuy2 = dst_stride_y = 0;
2478
0
  }
2479
0
#if defined(HAS_YUY2TOYROW_SSE2)
2480
0
  if (TestCpuFlag(kCpuHasSSE2)) {
2481
0
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
2482
0
    if (IS_ALIGNED(width, 16)) {
2483
0
      YUY2ToYRow = YUY2ToYRow_SSE2;
2484
0
    }
2485
0
  }
2486
0
#endif
2487
0
#if defined(HAS_YUY2TOYROW_AVX2)
2488
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2489
0
    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
2490
0
    if (IS_ALIGNED(width, 32)) {
2491
0
      YUY2ToYRow = YUY2ToYRow_AVX2;
2492
0
    }
2493
0
  }
2494
0
#endif
2495
#if defined(HAS_YUY2TOYROW_NEON)
2496
  if (TestCpuFlag(kCpuHasNEON)) {
2497
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
2498
    if (IS_ALIGNED(width, 16)) {
2499
      YUY2ToYRow = YUY2ToYRow_NEON;
2500
    }
2501
  }
2502
#endif
2503
#if defined(HAS_YUY2TOYROW_SVE2)
2504
  if (TestCpuFlag(kCpuHasSVE2)) {
2505
    YUY2ToYRow = YUY2ToYRow_SVE2;
2506
  }
2507
#endif
2508
2509
0
  for (y = 0; y < height; ++y) {
2510
0
    YUY2ToYRow(src_yuy2, dst_y, width);
2511
0
    src_yuy2 += src_stride_yuy2;
2512
0
    dst_y += dst_stride_y;
2513
0
  }
2514
0
  return 0;
2515
0
}
2516
2517
// Convert UYVY to Y.
2518
LIBYUV_API
2519
int UYVYToY(const uint8_t* src_uyvy,
2520
            int src_stride_uyvy,
2521
            uint8_t* dst_y,
2522
            int dst_stride_y,
2523
            int width,
2524
0
            int height) {
2525
0
  int y;
2526
0
  void (*UYVYToYRow)(const uint8_t* src_uyvy, uint8_t* dst_y, int width) =
2527
0
      UYVYToYRow_C;
2528
0
  if (!src_uyvy || !dst_y || width <= 0 || height == 0 || height == INT_MIN) {
2529
0
    return -1;
2530
0
  }
2531
  // Negative height means invert the image.
2532
0
  if (height < 0) {
2533
0
    height = -height;
2534
0
    src_uyvy = src_uyvy + (ptrdiff_t)(height - 1) * src_stride_uyvy;
2535
0
    src_stride_uyvy = -src_stride_uyvy;
2536
0
  }
2537
  // Coalesce rows.
2538
0
  if (src_stride_uyvy == width * 2 && dst_stride_y == width &&
2539
0
      (ptrdiff_t)width * height <= INT_MAX) {
2540
0
    width *= height;
2541
0
    height = 1;
2542
0
    src_stride_uyvy = dst_stride_y = 0;
2543
0
  }
2544
0
#if defined(HAS_UYVYTOYROW_SSE2)
2545
0
  if (TestCpuFlag(kCpuHasSSE2)) {
2546
0
    UYVYToYRow = UYVYToYRow_Any_SSE2;
2547
0
    if (IS_ALIGNED(width, 16)) {
2548
0
      UYVYToYRow = UYVYToYRow_SSE2;
2549
0
    }
2550
0
  }
2551
0
#endif
2552
0
#if defined(HAS_UYVYTOYROW_AVX2)
2553
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2554
0
    UYVYToYRow = UYVYToYRow_Any_AVX2;
2555
0
    if (IS_ALIGNED(width, 32)) {
2556
0
      UYVYToYRow = UYVYToYRow_AVX2;
2557
0
    }
2558
0
  }
2559
0
#endif
2560
#if defined(HAS_UYVYTOYROW_NEON)
2561
  if (TestCpuFlag(kCpuHasNEON)) {
2562
    UYVYToYRow = UYVYToYRow_Any_NEON;
2563
    if (IS_ALIGNED(width, 16)) {
2564
      UYVYToYRow = UYVYToYRow_NEON;
2565
    }
2566
  }
2567
#endif
2568
#if defined(HAS_UYVYTOYROW_SVE2)
2569
  if (TestCpuFlag(kCpuHasSVE2)) {
2570
    UYVYToYRow = UYVYToYRow_SVE2;
2571
  }
2572
#endif
2573
#if defined(HAS_UYVYTOYROW_LSX)
2574
  if (TestCpuFlag(kCpuHasLSX)) {
2575
    UYVYToYRow = UYVYToYRow_Any_LSX;
2576
    if (IS_ALIGNED(width, 16)) {
2577
      UYVYToYRow = UYVYToYRow_LSX;
2578
    }
2579
  }
2580
#endif
2581
2582
0
  for (y = 0; y < height; ++y) {
2583
0
    UYVYToYRow(src_uyvy, dst_y, width);
2584
0
    src_uyvy += src_stride_uyvy;
2585
0
    dst_y += dst_stride_y;
2586
0
  }
2587
0
  return 0;
2588
0
}
2589
2590
// Mirror a plane of data.
2591
// See Also I400Mirror
2592
LIBYUV_API
2593
void MirrorPlane(const uint8_t* src_y,
2594
                 int src_stride_y,
2595
                 uint8_t* dst_y,
2596
                 int dst_stride_y,
2597
                 int width,
2598
0
                 int height) {
2599
0
  int y;
2600
0
  void (*MirrorRow)(const uint8_t* src, uint8_t* dst, int width) = MirrorRow_C;
2601
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
2602
0
    return;
2603
0
  }
2604
  // Negative height means invert the image.
2605
0
  if (height < 0) {
2606
0
    height = -height;
2607
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
2608
0
    src_stride_y = -src_stride_y;
2609
0
  }
2610
#if defined(HAS_MIRRORROW_NEON)
2611
  if (TestCpuFlag(kCpuHasNEON)) {
2612
    MirrorRow = MirrorRow_Any_NEON;
2613
    if (IS_ALIGNED(width, 32)) {
2614
      MirrorRow = MirrorRow_NEON;
2615
    }
2616
  }
2617
#endif
2618
0
#if defined(HAS_MIRRORROW_SSSE3)
2619
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
2620
0
    MirrorRow = MirrorRow_Any_SSSE3;
2621
0
    if (IS_ALIGNED(width, 16)) {
2622
0
      MirrorRow = MirrorRow_SSSE3;
2623
0
    }
2624
0
  }
2625
0
#endif
2626
0
#if defined(HAS_MIRRORROW_AVX2)
2627
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2628
0
    MirrorRow = MirrorRow_Any_AVX2;
2629
0
    if (IS_ALIGNED(width, 32)) {
2630
0
      MirrorRow = MirrorRow_AVX2;
2631
0
    }
2632
0
  }
2633
0
#endif
2634
0
#if defined(HAS_MIRRORROW_AVX512BW)
2635
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
2636
0
    MirrorRow = MirrorRow_Any_AVX512BW;
2637
0
    if (IS_ALIGNED(width, 64)) {
2638
0
      MirrorRow = MirrorRow_AVX512BW;
2639
0
    }
2640
0
  }
2641
0
#endif
2642
#if defined(HAS_MIRRORROW_LSX)
2643
  if (TestCpuFlag(kCpuHasLSX)) {
2644
    MirrorRow = MirrorRow_Any_LSX;
2645
    if (IS_ALIGNED(width, 32)) {
2646
      MirrorRow = MirrorRow_LSX;
2647
    }
2648
  }
2649
#endif
2650
#if defined(HAS_MIRRORROW_LASX)
2651
  if (TestCpuFlag(kCpuHasLASX)) {
2652
    MirrorRow = MirrorRow_Any_LASX;
2653
    if (IS_ALIGNED(width, 64)) {
2654
      MirrorRow = MirrorRow_LASX;
2655
    }
2656
  }
2657
#endif
2658
2659
  // Mirror plane
2660
0
  for (y = 0; y < height; ++y) {
2661
0
    MirrorRow(src_y, dst_y, width);
2662
0
    src_y += src_stride_y;
2663
0
    dst_y += dst_stride_y;
2664
0
  }
2665
0
}
2666
2667
// Mirror a plane of UV data.
2668
LIBYUV_API
2669
void MirrorUVPlane(const uint8_t* src_uv,
2670
                   int src_stride_uv,
2671
                   uint8_t* dst_uv,
2672
                   int dst_stride_uv,
2673
                   int width,
2674
0
                   int height) {
2675
0
  int y;
2676
0
  void (*MirrorUVRow)(const uint8_t* src, uint8_t* dst, int width) =
2677
0
      MirrorUVRow_C;
2678
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
2679
0
    return;
2680
0
  }
2681
  // Negative height means invert the image.
2682
0
  if (height < 0) {
2683
0
    height = -height;
2684
0
    src_uv = src_uv + (ptrdiff_t)(height - 1) * src_stride_uv;
2685
0
    src_stride_uv = -src_stride_uv;
2686
0
  }
2687
#if defined(HAS_MIRRORUVROW_NEON)
2688
  if (TestCpuFlag(kCpuHasNEON)) {
2689
    MirrorUVRow = MirrorUVRow_Any_NEON;
2690
    if (IS_ALIGNED(width, 32)) {
2691
      MirrorUVRow = MirrorUVRow_NEON;
2692
    }
2693
  }
2694
#endif
2695
0
#if defined(HAS_MIRRORUVROW_SSSE3)
2696
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
2697
0
    if (IS_ALIGNED(width, 8)) {
2698
0
      MirrorUVRow = MirrorUVRow_SSSE3;
2699
0
    }
2700
0
  }
2701
0
#endif
2702
0
#if defined(HAS_MIRRORUVROW_AVX2)
2703
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2704
0
    MirrorUVRow = MirrorUVRow_Any_AVX2;
2705
0
    if (IS_ALIGNED(width, 16)) {
2706
0
      MirrorUVRow = MirrorUVRow_AVX2;
2707
0
    }
2708
0
  }
2709
0
#endif
2710
#if defined(HAS_MIRRORUVROW_LSX)
2711
  if (TestCpuFlag(kCpuHasLSX)) {
2712
    MirrorUVRow = MirrorUVRow_Any_LSX;
2713
    if (IS_ALIGNED(width, 8)) {
2714
      MirrorUVRow = MirrorUVRow_LSX;
2715
    }
2716
  }
2717
#endif
2718
#if defined(HAS_MIRRORUVROW_LASX)
2719
  if (TestCpuFlag(kCpuHasLASX)) {
2720
    MirrorUVRow = MirrorUVRow_Any_LASX;
2721
    if (IS_ALIGNED(width, 16)) {
2722
      MirrorUVRow = MirrorUVRow_LASX;
2723
    }
2724
  }
2725
#endif
2726
2727
  // MirrorUV plane
2728
0
  for (y = 0; y < height; ++y) {
2729
0
    MirrorUVRow(src_uv, dst_uv, width);
2730
0
    src_uv += src_stride_uv;
2731
0
    dst_uv += dst_stride_uv;
2732
0
  }
2733
0
}
2734
2735
// Mirror I400 with optional flipping
2736
LIBYUV_API
2737
int I400Mirror(const uint8_t* src_y,
2738
               int src_stride_y,
2739
               uint8_t* dst_y,
2740
               int dst_stride_y,
2741
               int width,
2742
0
               int height) {
2743
0
  if (!src_y || !dst_y || width <= 0 || height == 0 || height == INT_MIN) {
2744
0
    return -1;
2745
0
  }
2746
  // Negative height means invert the image.
2747
0
  if (height < 0) {
2748
0
    height = -height;
2749
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
2750
0
    src_stride_y = -src_stride_y;
2751
0
  }
2752
2753
0
  MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
2754
0
  return 0;
2755
0
}
2756
2757
// Mirror I420 with optional flipping
2758
LIBYUV_API
2759
int I420Mirror(const uint8_t* src_y,
2760
               int src_stride_y,
2761
               const uint8_t* src_u,
2762
               int src_stride_u,
2763
               const uint8_t* src_v,
2764
               int src_stride_v,
2765
               uint8_t* dst_y,
2766
               int dst_stride_y,
2767
               uint8_t* dst_u,
2768
               int dst_stride_u,
2769
               uint8_t* dst_v,
2770
               int dst_stride_v,
2771
               int width,
2772
0
               int height) {
2773
0
  int halfwidth = (width + 1) >> 1;
2774
0
  int halfheight = (height + 1) >> 1;
2775
2776
0
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
2777
0
      height == 0 || height == INT_MIN) {
2778
0
    return -1;
2779
0
  }
2780
2781
  // Negative height means invert the image.
2782
0
  if (height < 0) {
2783
0
    height = -height;
2784
0
    halfheight = (height + 1) >> 1;
2785
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
2786
0
    src_u = src_u + (ptrdiff_t)(halfheight - 1) * src_stride_u;
2787
0
    src_v = src_v + (ptrdiff_t)(halfheight - 1) * src_stride_v;
2788
0
    src_stride_y = -src_stride_y;
2789
0
    src_stride_u = -src_stride_u;
2790
0
    src_stride_v = -src_stride_v;
2791
0
  }
2792
2793
0
  if (dst_y) {
2794
0
    MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
2795
0
  }
2796
0
  MirrorPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
2797
0
  MirrorPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
2798
0
  return 0;
2799
0
}
2800
2801
// NV12 mirror.
2802
LIBYUV_API
2803
int NV12Mirror(const uint8_t* src_y,
2804
               int src_stride_y,
2805
               const uint8_t* src_uv,
2806
               int src_stride_uv,
2807
               uint8_t* dst_y,
2808
               int dst_stride_y,
2809
               uint8_t* dst_uv,
2810
               int dst_stride_uv,
2811
               int width,
2812
0
               int height) {
2813
0
  int halfwidth = (width + 1) >> 1;
2814
0
  int halfheight = (height + 1) >> 1;
2815
2816
0
  if ((!src_y && dst_y) || !src_uv || !dst_uv || width <= 0 || height == 0 ||
2817
0
      height == INT_MIN) {
2818
0
    return -1;
2819
0
  }
2820
2821
  // Negative height means invert the image.
2822
0
  if (height < 0) {
2823
0
    height = -height;
2824
0
    halfheight = (height + 1) >> 1;
2825
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
2826
0
    src_uv = src_uv + (ptrdiff_t)(halfheight - 1) * src_stride_uv;
2827
0
    src_stride_y = -src_stride_y;
2828
0
    src_stride_uv = -src_stride_uv;
2829
0
  }
2830
2831
0
  if (dst_y) {
2832
0
    MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
2833
0
  }
2834
0
  MirrorUVPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, halfwidth,
2835
0
                halfheight);
2836
0
  return 0;
2837
0
}
2838
2839
// ARGB mirror.
2840
LIBYUV_API
2841
int ARGBMirror(const uint8_t* src_argb,
2842
               int src_stride_argb,
2843
               uint8_t* dst_argb,
2844
               int dst_stride_argb,
2845
               int width,
2846
0
               int height) {
2847
0
  int y;
2848
0
  void (*ARGBMirrorRow)(const uint8_t* src, uint8_t* dst, int width) =
2849
0
      ARGBMirrorRow_C;
2850
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
2851
0
      height == INT_MIN) {
2852
0
    return -1;
2853
0
  }
2854
  // Negative height means invert the image.
2855
0
  if (height < 0) {
2856
0
    height = -height;
2857
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
2858
0
    src_stride_argb = -src_stride_argb;
2859
0
  }
2860
#if defined(HAS_ARGBMIRRORROW_NEON)
2861
  if (TestCpuFlag(kCpuHasNEON)) {
2862
    ARGBMirrorRow = ARGBMirrorRow_Any_NEON;
2863
    if (IS_ALIGNED(width, 8)) {
2864
      ARGBMirrorRow = ARGBMirrorRow_NEON;
2865
    }
2866
  }
2867
#endif
2868
0
#if defined(HAS_ARGBMIRRORROW_SSE2)
2869
0
  if (TestCpuFlag(kCpuHasSSE2)) {
2870
0
    ARGBMirrorRow = ARGBMirrorRow_Any_SSE2;
2871
0
    if (IS_ALIGNED(width, 4)) {
2872
0
      ARGBMirrorRow = ARGBMirrorRow_SSE2;
2873
0
    }
2874
0
  }
2875
0
#endif
2876
0
#if defined(HAS_ARGBMIRRORROW_AVX2)
2877
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2878
0
    ARGBMirrorRow = ARGBMirrorRow_Any_AVX2;
2879
0
    if (IS_ALIGNED(width, 8)) {
2880
0
      ARGBMirrorRow = ARGBMirrorRow_AVX2;
2881
0
    }
2882
0
  }
2883
0
#endif
2884
#if defined(HAS_ARGBMIRRORROW_LSX)
2885
  if (TestCpuFlag(kCpuHasLSX)) {
2886
    ARGBMirrorRow = ARGBMirrorRow_Any_LSX;
2887
    if (IS_ALIGNED(width, 8)) {
2888
      ARGBMirrorRow = ARGBMirrorRow_LSX;
2889
    }
2890
  }
2891
#endif
2892
#if defined(HAS_ARGBMIRRORROW_LASX)
2893
  if (TestCpuFlag(kCpuHasLASX)) {
2894
    ARGBMirrorRow = ARGBMirrorRow_Any_LASX;
2895
    if (IS_ALIGNED(width, 16)) {
2896
      ARGBMirrorRow = ARGBMirrorRow_LASX;
2897
    }
2898
  }
2899
#endif
2900
2901
  // Mirror plane
2902
0
  for (y = 0; y < height; ++y) {
2903
0
    ARGBMirrorRow(src_argb, dst_argb, width);
2904
0
    src_argb += src_stride_argb;
2905
0
    dst_argb += dst_stride_argb;
2906
0
  }
2907
0
  return 0;
2908
0
}
2909
2910
// RGB24 mirror.
2911
LIBYUV_API
2912
int RGB24Mirror(const uint8_t* src_rgb24,
2913
                int src_stride_rgb24,
2914
                uint8_t* dst_rgb24,
2915
                int dst_stride_rgb24,
2916
                int width,
2917
0
                int height) {
2918
0
  int y;
2919
0
  void (*RGB24MirrorRow)(const uint8_t* src, uint8_t* dst, int width) =
2920
0
      RGB24MirrorRow_C;
2921
0
  if (!src_rgb24 || !dst_rgb24 || width <= 0 || height == 0 ||
2922
0
      height == INT_MIN) {
2923
0
    return -1;
2924
0
  }
2925
  // Negative height means invert the image.
2926
0
  if (height < 0) {
2927
0
    height = -height;
2928
0
    src_rgb24 = src_rgb24 + (ptrdiff_t)(height - 1) * src_stride_rgb24;
2929
0
    src_stride_rgb24 = -src_stride_rgb24;
2930
0
  }
2931
#if defined(HAS_RGB24MIRRORROW_NEON)
2932
  if (TestCpuFlag(kCpuHasNEON)) {
2933
    RGB24MirrorRow = RGB24MirrorRow_Any_NEON;
2934
    if (IS_ALIGNED(width, 16)) {
2935
      RGB24MirrorRow = RGB24MirrorRow_NEON;
2936
    }
2937
  }
2938
#endif
2939
0
#if defined(HAS_RGB24MIRRORROW_AVX2)
2940
0
  if (TestCpuFlag(kCpuHasAVX2)) {
2941
0
    RGB24MirrorRow = RGB24MirrorRow_Any_AVX2;
2942
0
    if (IS_ALIGNED(width, 32)) {
2943
0
      RGB24MirrorRow = RGB24MirrorRow_AVX2;
2944
0
    }
2945
0
  }
2946
0
#endif
2947
2948
  // Mirror plane
2949
0
  for (y = 0; y < height; ++y) {
2950
0
    RGB24MirrorRow(src_rgb24, dst_rgb24, width);
2951
0
    src_rgb24 += src_stride_rgb24;
2952
0
    dst_rgb24 += dst_stride_rgb24;
2953
0
  }
2954
0
  return 0;
2955
0
}
2956
2957
// Alpha Blend 2 ARGB images and store to destination.
2958
LIBYUV_API
2959
int ARGBBlend(const uint8_t* src_argb0,
2960
              int src_stride_argb0,
2961
              const uint8_t* src_argb1,
2962
              int src_stride_argb1,
2963
              uint8_t* dst_argb,
2964
              int dst_stride_argb,
2965
              int width,
2966
0
              int height) {
2967
0
  int y;
2968
0
  void (*ARGBBlendRow)(const uint8_t* src_argb, const uint8_t* src_argb1,
2969
0
                       uint8_t* dst_argb, int width) = ARGBBlendRow_C;
2970
0
  if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0 ||
2971
0
      height == INT_MIN) {
2972
0
    return -1;
2973
0
  }
2974
  // Negative height means invert the image.
2975
0
  if (height < 0) {
2976
0
    height = -height;
2977
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
2978
0
    dst_stride_argb = -dst_stride_argb;
2979
0
  }
2980
  // Coalesce rows.
2981
0
  if (src_stride_argb0 == width * 4 && src_stride_argb1 == width * 4 &&
2982
0
      dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
2983
0
    width *= height;
2984
0
    height = 1;
2985
0
    src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
2986
0
  }
2987
0
#if defined(HAS_ARGBBLENDROW_SSSE3)
2988
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
2989
0
    ARGBBlendRow = ARGBBlendRow_SSSE3;
2990
0
  }
2991
0
#endif
2992
#if defined(HAS_ARGBBLENDROW_NEON)
2993
  if (TestCpuFlag(kCpuHasNEON)) {
2994
    ARGBBlendRow = ARGBBlendRow_NEON;
2995
  }
2996
#endif
2997
#if defined(HAS_ARGBBLENDROW_LSX)
2998
  if (TestCpuFlag(kCpuHasLSX)) {
2999
    ARGBBlendRow = ARGBBlendRow_LSX;
3000
  }
3001
#endif
3002
#if defined(HAS_ARGBBLENDROW_RVV)
3003
  if (TestCpuFlag(kCpuHasRVV)) {
3004
    ARGBBlendRow = ARGBBlendRow_RVV;
3005
  }
3006
#endif
3007
0
  for (y = 0; y < height; ++y) {
3008
0
    ARGBBlendRow(src_argb0, src_argb1, dst_argb, width);
3009
0
    src_argb0 += src_stride_argb0;
3010
0
    src_argb1 += src_stride_argb1;
3011
0
    dst_argb += dst_stride_argb;
3012
0
  }
3013
0
  return 0;
3014
0
}
3015
3016
// Alpha Blend plane and store to destination.
3017
LIBYUV_API
3018
int BlendPlane(const uint8_t* src_y0,
3019
               int src_stride_y0,
3020
               const uint8_t* src_y1,
3021
               int src_stride_y1,
3022
               const uint8_t* alpha,
3023
               int alpha_stride,
3024
               uint8_t* dst_y,
3025
               int dst_stride_y,
3026
               int width,
3027
0
               int height) {
3028
0
  int y;
3029
0
  void (*BlendPlaneRow)(const uint8_t* src0, const uint8_t* src1,
3030
0
                        const uint8_t* alpha, uint8_t* dst, int width) =
3031
0
      BlendPlaneRow_C;
3032
0
  if (!src_y0 || !src_y1 || !alpha || !dst_y || width <= 0 || height == 0 ||
3033
0
      height == INT_MIN) {
3034
0
    return -1;
3035
0
  }
3036
  // Negative height means invert the image.
3037
0
  if (height < 0) {
3038
0
    height = -height;
3039
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
3040
0
    dst_stride_y = -dst_stride_y;
3041
0
  }
3042
3043
  // Coalesce rows for Y plane.
3044
0
  if (src_stride_y0 == width && src_stride_y1 == width &&
3045
0
      alpha_stride == width && dst_stride_y == width &&
3046
0
      (ptrdiff_t)width * height <= INT_MAX) {
3047
0
    width *= height;
3048
0
    height = 1;
3049
0
    src_stride_y0 = src_stride_y1 = alpha_stride = dst_stride_y = 0;
3050
0
  }
3051
3052
0
#if defined(HAS_BLENDPLANEROW_SSSE3)
3053
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
3054
0
    BlendPlaneRow = BlendPlaneRow_Any_SSSE3;
3055
0
    if (IS_ALIGNED(width, 8)) {
3056
0
      BlendPlaneRow = BlendPlaneRow_SSSE3;
3057
0
    }
3058
0
  }
3059
0
#endif
3060
0
#if defined(HAS_BLENDPLANEROW_AVX2)
3061
0
  if (TestCpuFlag(kCpuHasAVX2)) {
3062
0
    BlendPlaneRow = BlendPlaneRow_Any_AVX2;
3063
0
    if (IS_ALIGNED(width, 32)) {
3064
0
      BlendPlaneRow = BlendPlaneRow_AVX2;
3065
0
    }
3066
0
  }
3067
0
#endif
3068
#if defined(HAS_BLENDPLANEROW_RVV)
3069
  if (TestCpuFlag(kCpuHasRVV)) {
3070
    BlendPlaneRow = BlendPlaneRow_RVV;
3071
  }
3072
#endif
3073
3074
0
  for (y = 0; y < height; ++y) {
3075
0
    BlendPlaneRow(src_y0, src_y1, alpha, dst_y, width);
3076
0
    src_y0 += src_stride_y0;
3077
0
    src_y1 += src_stride_y1;
3078
0
    alpha += alpha_stride;
3079
0
    dst_y += dst_stride_y;
3080
0
  }
3081
0
  return 0;
3082
0
}
3083
3084
#define MAXTWIDTH 2048
3085
// Alpha Blend YUV images and store to destination.
3086
LIBYUV_API
3087
int I420Blend(const uint8_t* src_y0,
3088
              int src_stride_y0,
3089
              const uint8_t* src_u0,
3090
              int src_stride_u0,
3091
              const uint8_t* src_v0,
3092
              int src_stride_v0,
3093
              const uint8_t* src_y1,
3094
              int src_stride_y1,
3095
              const uint8_t* src_u1,
3096
              int src_stride_u1,
3097
              const uint8_t* src_v1,
3098
              int src_stride_v1,
3099
              const uint8_t* alpha,
3100
              int alpha_stride,
3101
              uint8_t* dst_y,
3102
              int dst_stride_y,
3103
              uint8_t* dst_u,
3104
              int dst_stride_u,
3105
              uint8_t* dst_v,
3106
              int dst_stride_v,
3107
              int width,
3108
0
              int height) {
3109
0
  int y;
3110
  // Half width/height for UV.
3111
0
  int halfwidth = (width + 1) >> 1;
3112
0
  void (*BlendPlaneRow)(const uint8_t* src0, const uint8_t* src1,
3113
0
                        const uint8_t* alpha, uint8_t* dst, int width) =
3114
0
      BlendPlaneRow_C;
3115
0
  void (*ScaleRowDown2)(const uint8_t* src_ptr, ptrdiff_t src_stride,
3116
0
                        uint8_t* dst_ptr, int dst_width) = ScaleRowDown2Box_C;
3117
3118
0
  if (!src_y0 || !src_u0 || !src_v0 || !src_y1 || !src_u1 || !src_v1 ||
3119
0
      !alpha || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0 ||
3120
0
      height == INT_MIN) {
3121
0
    return -1;
3122
0
  }
3123
3124
  // Negative height means invert the image.
3125
0
  if (height < 0) {
3126
0
    height = -height;
3127
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
3128
0
    dst_stride_y = -dst_stride_y;
3129
0
  }
3130
3131
  // Blend Y plane.
3132
0
  BlendPlane(src_y0, src_stride_y0, src_y1, src_stride_y1, alpha, alpha_stride,
3133
0
             dst_y, dst_stride_y, width, height);
3134
3135
0
#if defined(HAS_BLENDPLANEROW_SSSE3)
3136
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
3137
0
    BlendPlaneRow = BlendPlaneRow_Any_SSSE3;
3138
0
    if (IS_ALIGNED(halfwidth, 8)) {
3139
0
      BlendPlaneRow = BlendPlaneRow_SSSE3;
3140
0
    }
3141
0
  }
3142
0
#endif
3143
0
#if defined(HAS_BLENDPLANEROW_AVX2)
3144
0
  if (TestCpuFlag(kCpuHasAVX2)) {
3145
0
    BlendPlaneRow = BlendPlaneRow_Any_AVX2;
3146
0
    if (IS_ALIGNED(halfwidth, 32)) {
3147
0
      BlendPlaneRow = BlendPlaneRow_AVX2;
3148
0
    }
3149
0
  }
3150
0
#endif
3151
#if defined(HAS_BLENDPLANEROW_RVV)
3152
  if (TestCpuFlag(kCpuHasRVV)) {
3153
    BlendPlaneRow = BlendPlaneRow_RVV;
3154
  }
3155
#endif
3156
0
  if (!IS_ALIGNED(width, 2)) {
3157
0
    ScaleRowDown2 = ScaleRowDown2Box_Odd_C;
3158
0
  }
3159
#if defined(HAS_SCALEROWDOWN2_NEON)
3160
  if (TestCpuFlag(kCpuHasNEON)) {
3161
    ScaleRowDown2 = ScaleRowDown2Box_Odd_NEON;
3162
    if (IS_ALIGNED(width, 2)) {
3163
      ScaleRowDown2 = ScaleRowDown2Box_Any_NEON;
3164
      if (IS_ALIGNED(halfwidth, 16)) {
3165
        ScaleRowDown2 = ScaleRowDown2Box_NEON;
3166
      }
3167
    }
3168
  }
3169
#endif
3170
0
#if defined(HAS_SCALEROWDOWN2_SSSE3)
3171
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
3172
0
    ScaleRowDown2 = ScaleRowDown2Box_Odd_SSSE3;
3173
0
    if (IS_ALIGNED(width, 2)) {
3174
0
      ScaleRowDown2 = ScaleRowDown2Box_Any_SSSE3;
3175
0
      if (IS_ALIGNED(halfwidth, 16)) {
3176
0
        ScaleRowDown2 = ScaleRowDown2Box_SSSE3;
3177
0
      }
3178
0
    }
3179
0
  }
3180
0
#endif
3181
0
#if defined(HAS_SCALEROWDOWN2_AVX2)
3182
0
  if (TestCpuFlag(kCpuHasAVX2)) {
3183
0
    ScaleRowDown2 = ScaleRowDown2Box_Odd_AVX2;
3184
0
    if (IS_ALIGNED(width, 2)) {
3185
0
      ScaleRowDown2 = ScaleRowDown2Box_Any_AVX2;
3186
0
      if (IS_ALIGNED(halfwidth, 32)) {
3187
0
        ScaleRowDown2 = ScaleRowDown2Box_AVX2;
3188
0
      }
3189
0
    }
3190
0
  }
3191
0
#endif
3192
#if defined(HAS_SCALEROWDOWN2_RVV)
3193
  if (TestCpuFlag(kCpuHasRVV)) {
3194
    ScaleRowDown2 = ScaleRowDown2Box_RVV;
3195
  }
3196
#endif
3197
3198
  // Row buffer for intermediate alpha pixels.
3199
0
  align_buffer_64(halfalpha, halfwidth);
3200
0
  if (!halfalpha)
3201
0
    return 1;
3202
0
  for (y = 0; y < height; y += 2) {
3203
    // last row of odd height image use 1 row of alpha instead of 2.
3204
0
    if (y == (height - 1)) {
3205
0
      alpha_stride = 0;
3206
0
    }
3207
    // Subsample 2 rows of UV to half width and half height.
3208
0
    ScaleRowDown2(alpha, alpha_stride, halfalpha, halfwidth);
3209
0
    alpha += (ptrdiff_t)alpha_stride * 2;
3210
0
    BlendPlaneRow(src_u0, src_u1, halfalpha, dst_u, halfwidth);
3211
0
    BlendPlaneRow(src_v0, src_v1, halfalpha, dst_v, halfwidth);
3212
0
    src_u0 += src_stride_u0;
3213
0
    src_u1 += src_stride_u1;
3214
0
    dst_u += dst_stride_u;
3215
0
    src_v0 += src_stride_v0;
3216
0
    src_v1 += src_stride_v1;
3217
0
    dst_v += dst_stride_v;
3218
0
  }
3219
0
  free_aligned_buffer_64(halfalpha);
3220
0
  return 0;
3221
0
}
3222
3223
// Multiply 2 ARGB images and store to destination.
3224
LIBYUV_API
3225
int ARGBMultiply(const uint8_t* src_argb0,
3226
                 int src_stride_argb0,
3227
                 const uint8_t* src_argb1,
3228
                 int src_stride_argb1,
3229
                 uint8_t* dst_argb,
3230
                 int dst_stride_argb,
3231
                 int width,
3232
0
                 int height) {
3233
0
  int y;
3234
0
  void (*ARGBMultiplyRow)(const uint8_t* src0, const uint8_t* src1,
3235
0
                          uint8_t* dst, int width) = ARGBMultiplyRow_C;
3236
0
  if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0 ||
3237
0
      height == INT_MIN) {
3238
0
    return -1;
3239
0
  }
3240
  // Negative height means invert the image.
3241
0
  if (height < 0) {
3242
0
    height = -height;
3243
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
3244
0
    dst_stride_argb = -dst_stride_argb;
3245
0
  }
3246
  // Coalesce rows.
3247
0
  if (src_stride_argb0 == width * 4 && src_stride_argb1 == width * 4 &&
3248
0
      dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
3249
0
    width *= height;
3250
0
    height = 1;
3251
0
    src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
3252
0
  }
3253
#if defined(HAS_ARGBMULTIPLYROW_SSE2)
3254
  if (TestCpuFlag(kCpuHasSSE2)) {
3255
    ARGBMultiplyRow = ARGBMultiplyRow_Any_SSE2;
3256
    if (IS_ALIGNED(width, 4)) {
3257
      ARGBMultiplyRow = ARGBMultiplyRow_SSE2;
3258
    }
3259
  }
3260
#endif
3261
#if defined(HAS_ARGBMULTIPLYROW_AVX2)
3262
  if (TestCpuFlag(kCpuHasAVX2)) {
3263
    ARGBMultiplyRow = ARGBMultiplyRow_Any_AVX2;
3264
    if (IS_ALIGNED(width, 8)) {
3265
      ARGBMultiplyRow = ARGBMultiplyRow_AVX2;
3266
    }
3267
  }
3268
#endif
3269
#if defined(HAS_ARGBMULTIPLYROW_NEON)
3270
  if (TestCpuFlag(kCpuHasNEON)) {
3271
    ARGBMultiplyRow = ARGBMultiplyRow_Any_NEON;
3272
    if (IS_ALIGNED(width, 8)) {
3273
      ARGBMultiplyRow = ARGBMultiplyRow_NEON;
3274
    }
3275
  }
3276
#endif
3277
#if defined(HAS_ARGBMULTIPLYROW_SME)
3278
  if (TestCpuFlag(kCpuHasSME)) {
3279
    ARGBMultiplyRow = ARGBMultiplyRow_SME;
3280
  }
3281
#endif
3282
#if defined(HAS_ARGBMULTIPLYROW_LSX)
3283
  if (TestCpuFlag(kCpuHasLSX)) {
3284
    ARGBMultiplyRow = ARGBMultiplyRow_Any_LSX;
3285
    if (IS_ALIGNED(width, 4)) {
3286
      ARGBMultiplyRow = ARGBMultiplyRow_LSX;
3287
    }
3288
  }
3289
#endif
3290
#if defined(HAS_ARGBMULTIPLYROW_LASX)
3291
  if (TestCpuFlag(kCpuHasLASX)) {
3292
    ARGBMultiplyRow = ARGBMultiplyRow_Any_LASX;
3293
    if (IS_ALIGNED(width, 8)) {
3294
      ARGBMultiplyRow = ARGBMultiplyRow_LASX;
3295
    }
3296
  }
3297
#endif
3298
3299
  // Multiply plane
3300
0
  for (y = 0; y < height; ++y) {
3301
0
    ARGBMultiplyRow(src_argb0, src_argb1, dst_argb, width);
3302
0
    src_argb0 += src_stride_argb0;
3303
0
    src_argb1 += src_stride_argb1;
3304
0
    dst_argb += dst_stride_argb;
3305
0
  }
3306
0
  return 0;
3307
0
}
3308
3309
// Add 2 ARGB images and store to destination.
3310
LIBYUV_API
3311
int ARGBAdd(const uint8_t* src_argb0,
3312
            int src_stride_argb0,
3313
            const uint8_t* src_argb1,
3314
            int src_stride_argb1,
3315
            uint8_t* dst_argb,
3316
            int dst_stride_argb,
3317
            int width,
3318
0
            int height) {
3319
0
  int y;
3320
0
  void (*ARGBAddRow)(const uint8_t* src0, const uint8_t* src1, uint8_t* dst,
3321
0
                     int width) = ARGBAddRow_C;
3322
0
  if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0 ||
3323
0
      height == INT_MIN) {
3324
0
    return -1;
3325
0
  }
3326
  // Negative height means invert the image.
3327
0
  if (height < 0) {
3328
0
    height = -height;
3329
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
3330
0
    dst_stride_argb = -dst_stride_argb;
3331
0
  }
3332
  // Coalesce rows.
3333
0
  if (src_stride_argb0 == width * 4 && src_stride_argb1 == width * 4 &&
3334
0
      dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
3335
0
    width *= height;
3336
0
    height = 1;
3337
0
    src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
3338
0
  }
3339
0
#if defined(HAS_ARGBADDROW_SSE2)
3340
0
  if (TestCpuFlag(kCpuHasSSE2)) {
3341
0
    ARGBAddRow = ARGBAddRow_SSE2;
3342
0
  }
3343
0
#endif
3344
0
#if defined(HAS_ARGBADDROW_SSE2)
3345
0
  if (TestCpuFlag(kCpuHasSSE2)) {
3346
0
    ARGBAddRow = ARGBAddRow_Any_SSE2;
3347
0
    if (IS_ALIGNED(width, 4)) {
3348
0
      ARGBAddRow = ARGBAddRow_SSE2;
3349
0
    }
3350
0
  }
3351
0
#endif
3352
0
#if defined(HAS_ARGBADDROW_AVX2)
3353
0
  if (TestCpuFlag(kCpuHasAVX2)) {
3354
0
    ARGBAddRow = ARGBAddRow_Any_AVX2;
3355
0
    if (IS_ALIGNED(width, 8)) {
3356
0
      ARGBAddRow = ARGBAddRow_AVX2;
3357
0
    }
3358
0
  }
3359
0
#endif
3360
#if defined(HAS_ARGBADDROW_NEON)
3361
  if (TestCpuFlag(kCpuHasNEON)) {
3362
    ARGBAddRow = ARGBAddRow_Any_NEON;
3363
    if (IS_ALIGNED(width, 8)) {
3364
      ARGBAddRow = ARGBAddRow_NEON;
3365
    }
3366
  }
3367
#endif
3368
#if defined(HAS_ARGBADDROW_LSX)
3369
  if (TestCpuFlag(kCpuHasLSX)) {
3370
    ARGBAddRow = ARGBAddRow_Any_LSX;
3371
    if (IS_ALIGNED(width, 4)) {
3372
      ARGBAddRow = ARGBAddRow_LSX;
3373
    }
3374
  }
3375
#endif
3376
#if defined(HAS_ARGBADDROW_LASX)
3377
  if (TestCpuFlag(kCpuHasLASX)) {
3378
    ARGBAddRow = ARGBAddRow_Any_LASX;
3379
    if (IS_ALIGNED(width, 8)) {
3380
      ARGBAddRow = ARGBAddRow_LASX;
3381
    }
3382
  }
3383
#endif
3384
3385
  // Add plane
3386
0
  for (y = 0; y < height; ++y) {
3387
0
    ARGBAddRow(src_argb0, src_argb1, dst_argb, width);
3388
0
    src_argb0 += src_stride_argb0;
3389
0
    src_argb1 += src_stride_argb1;
3390
0
    dst_argb += dst_stride_argb;
3391
0
  }
3392
0
  return 0;
3393
0
}
3394
3395
// Subtract 2 ARGB images and store to destination.
3396
LIBYUV_API
3397
int ARGBSubtract(const uint8_t* src_argb0,
3398
                 int src_stride_argb0,
3399
                 const uint8_t* src_argb1,
3400
                 int src_stride_argb1,
3401
                 uint8_t* dst_argb,
3402
                 int dst_stride_argb,
3403
                 int width,
3404
0
                 int height) {
3405
0
  int y;
3406
0
  void (*ARGBSubtractRow)(const uint8_t* src0, const uint8_t* src1,
3407
0
                          uint8_t* dst, int width) = ARGBSubtractRow_C;
3408
0
  if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0 ||
3409
0
      height == INT_MIN) {
3410
0
    return -1;
3411
0
  }
3412
  // Negative height means invert the image.
3413
0
  if (height < 0) {
3414
0
    height = -height;
3415
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
3416
0
    dst_stride_argb = -dst_stride_argb;
3417
0
  }
3418
  // Coalesce rows.
3419
0
  if (src_stride_argb0 == width * 4 && src_stride_argb1 == width * 4 &&
3420
0
      dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
3421
0
    width *= height;
3422
0
    height = 1;
3423
0
    src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
3424
0
  }
3425
0
#if defined(HAS_ARGBSUBTRACTROW_SSE2)
3426
0
  if (TestCpuFlag(kCpuHasSSE2)) {
3427
0
    ARGBSubtractRow = ARGBSubtractRow_Any_SSE2;
3428
0
    if (IS_ALIGNED(width, 4)) {
3429
0
      ARGBSubtractRow = ARGBSubtractRow_SSE2;
3430
0
    }
3431
0
  }
3432
0
#endif
3433
0
#if defined(HAS_ARGBSUBTRACTROW_AVX2)
3434
0
  if (TestCpuFlag(kCpuHasAVX2)) {
3435
0
    ARGBSubtractRow = ARGBSubtractRow_Any_AVX2;
3436
0
    if (IS_ALIGNED(width, 8)) {
3437
0
      ARGBSubtractRow = ARGBSubtractRow_AVX2;
3438
0
    }
3439
0
  }
3440
0
#endif
3441
#if defined(HAS_ARGBSUBTRACTROW_NEON)
3442
  if (TestCpuFlag(kCpuHasNEON)) {
3443
    ARGBSubtractRow = ARGBSubtractRow_Any_NEON;
3444
    if (IS_ALIGNED(width, 8)) {
3445
      ARGBSubtractRow = ARGBSubtractRow_NEON;
3446
    }
3447
  }
3448
#endif
3449
#if defined(HAS_ARGBSUBTRACTROW_LSX)
3450
  if (TestCpuFlag(kCpuHasLSX)) {
3451
    ARGBSubtractRow = ARGBSubtractRow_Any_LSX;
3452
    if (IS_ALIGNED(width, 4)) {
3453
      ARGBSubtractRow = ARGBSubtractRow_LSX;
3454
    }
3455
  }
3456
#endif
3457
#if defined(HAS_ARGBSUBTRACTROW_LASX)
3458
  if (TestCpuFlag(kCpuHasLASX)) {
3459
    ARGBSubtractRow = ARGBSubtractRow_Any_LASX;
3460
    if (IS_ALIGNED(width, 8)) {
3461
      ARGBSubtractRow = ARGBSubtractRow_LASX;
3462
    }
3463
  }
3464
#endif
3465
3466
  // Subtract plane
3467
0
  for (y = 0; y < height; ++y) {
3468
0
    ARGBSubtractRow(src_argb0, src_argb1, dst_argb, width);
3469
0
    src_argb0 += src_stride_argb0;
3470
0
    src_argb1 += src_stride_argb1;
3471
0
    dst_argb += dst_stride_argb;
3472
0
  }
3473
0
  return 0;
3474
0
}
3475
3476
// Convert RAW to RGB24.
3477
LIBYUV_API
3478
int RAWToRGB24(const uint8_t* src_raw,
3479
               int src_stride_raw,
3480
               uint8_t* dst_rgb24,
3481
               int dst_stride_rgb24,
3482
               int width,
3483
0
               int height) {
3484
0
  int y;
3485
0
  void (*RAWToRGB24Row)(const uint8_t* src_rgb, uint8_t* dst_rgb24, int width) =
3486
0
      RAWToRGB24Row_C;
3487
0
  if (!src_raw || !dst_rgb24 || width <= 0 || height == 0 ||
3488
0
      height == INT_MIN) {
3489
0
    return -1;
3490
0
  }
3491
  // Negative height means invert the image.
3492
0
  if (height < 0) {
3493
0
    height = -height;
3494
0
    src_raw = src_raw + (ptrdiff_t)(height - 1) * src_stride_raw;
3495
0
    src_stride_raw = -src_stride_raw;
3496
0
  }
3497
  // Coalesce rows.
3498
0
  if (src_stride_raw == width * 3 && dst_stride_rgb24 == width * 3 &&
3499
0
      (ptrdiff_t)width * height <= INT_MAX) {
3500
0
    width *= height;
3501
0
    height = 1;
3502
0
    src_stride_raw = dst_stride_rgb24 = 0;
3503
0
  }
3504
0
#if defined(HAS_RAWTORGB24ROW_SSSE3)
3505
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
3506
0
    RAWToRGB24Row = RAWToRGB24Row_Any_SSSE3;
3507
0
    if (IS_ALIGNED(width, 8)) {
3508
0
      RAWToRGB24Row = RAWToRGB24Row_SSSE3;
3509
0
    }
3510
0
  }
3511
0
#endif
3512
#if defined(HAS_RAWTORGB24ROW_NEON)
3513
  if (TestCpuFlag(kCpuHasNEON)) {
3514
    RAWToRGB24Row = RAWToRGB24Row_Any_NEON;
3515
    if (IS_ALIGNED(width, 8)) {
3516
      RAWToRGB24Row = RAWToRGB24Row_NEON;
3517
    }
3518
  }
3519
#endif
3520
#if defined(HAS_RAWTORGB24ROW_SVE2)
3521
  if (TestCpuFlag(kCpuHasSVE2)) {
3522
    RAWToRGB24Row = RAWToRGB24Row_SVE2;
3523
  }
3524
#endif
3525
#if defined(HAS_RAWTORGB24ROW_LSX)
3526
  if (TestCpuFlag(kCpuHasLSX)) {
3527
    RAWToRGB24Row = RAWToRGB24Row_Any_LSX;
3528
    if (IS_ALIGNED(width, 16)) {
3529
      RAWToRGB24Row = RAWToRGB24Row_LSX;
3530
    }
3531
  }
3532
#endif
3533
#if defined(HAS_RAWTORGB24ROW_RVV)
3534
  if (TestCpuFlag(kCpuHasRVV)) {
3535
    RAWToRGB24Row = RAWToRGB24Row_RVV;
3536
  }
3537
#endif
3538
3539
0
  for (y = 0; y < height; ++y) {
3540
0
    RAWToRGB24Row(src_raw, dst_rgb24, width);
3541
0
    src_raw += src_stride_raw;
3542
0
    dst_rgb24 += dst_stride_rgb24;
3543
0
  }
3544
0
  return 0;
3545
0
}
3546
3547
// TODO(fbarchard): Consider uint8_t value
3548
LIBYUV_API
3549
void SetPlane(uint8_t* dst_y,
3550
              int dst_stride_y,
3551
              int width,
3552
              int height,
3553
0
              uint32_t value) {
3554
0
  int y;
3555
0
  void (*SetRow)(uint8_t* dst, uint8_t value, int width) = SetRow_C;
3556
3557
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
3558
0
    return;
3559
0
  }
3560
0
  if (height < 0) {
3561
0
    height = -height;
3562
0
    dst_y = dst_y + (ptrdiff_t)(height - 1) * dst_stride_y;
3563
0
    dst_stride_y = -dst_stride_y;
3564
0
  }
3565
  // Coalesce rows.
3566
0
  if (dst_stride_y == width && (ptrdiff_t)width * height <= INT_MAX) {
3567
0
    width *= height;
3568
0
    height = 1;
3569
0
    dst_stride_y = 0;
3570
0
  }
3571
#if defined(HAS_SETROW_NEON)
3572
  if (TestCpuFlag(kCpuHasNEON)) {
3573
    SetRow = SetRow_Any_NEON;
3574
    if (IS_ALIGNED(width, 16)) {
3575
      SetRow = SetRow_NEON;
3576
    }
3577
  }
3578
#endif
3579
0
#if defined(HAS_SETROW_X86)
3580
0
  if (TestCpuFlag(kCpuHasX86)) {
3581
0
    SetRow = SetRow_Any_X86;
3582
0
    if (IS_ALIGNED(width, 4)) {
3583
0
      SetRow = SetRow_X86;
3584
0
    }
3585
0
  }
3586
0
#endif
3587
0
#if defined(HAS_SETROW_ERMS)
3588
0
  if (TestCpuFlag(kCpuHasERMS)) {
3589
0
    SetRow = SetRow_ERMS;
3590
0
  }
3591
0
#endif
3592
#if defined(HAS_SETROW_LSX)
3593
  if (TestCpuFlag(kCpuHasLSX)) {
3594
    SetRow = SetRow_Any_LSX;
3595
    if (IS_ALIGNED(width, 16)) {
3596
      SetRow = SetRow_LSX;
3597
    }
3598
  }
3599
#endif
3600
3601
  // Set plane
3602
0
  for (y = 0; y < height; ++y) {
3603
0
    SetRow(dst_y, (uint8_t)value, width);
3604
0
    dst_y += dst_stride_y;
3605
0
  }
3606
0
}
3607
3608
// Draw a rectangle into I420
3609
LIBYUV_API
3610
int I420Rect(uint8_t* dst_y,
3611
             int dst_stride_y,
3612
             uint8_t* dst_u,
3613
             int dst_stride_u,
3614
             uint8_t* dst_v,
3615
             int dst_stride_v,
3616
             int x,
3617
             int y,
3618
             int width,
3619
             int height,
3620
             int value_y,
3621
             int value_u,
3622
0
             int value_v) {
3623
0
  int halfwidth = (width + 1) >> 1;
3624
0
  int halfheight = (height + 1) >> 1;
3625
0
  uint8_t* start_y = dst_y + y * dst_stride_y + x;
3626
0
  uint8_t* start_u = dst_u + (y / 2) * dst_stride_u + (x / 2);
3627
0
  uint8_t* start_v = dst_v + (y / 2) * dst_stride_v + (x / 2);
3628
3629
0
  if (!dst_y || !dst_u || !dst_v || width <= 0 || height == 0 ||
3630
0
      height == INT_MIN || x < 0 || y < 0 || value_y < 0 || value_y > 255 ||
3631
0
      value_u < 0 || value_u > 255 || value_v < 0 || value_v > 255) {
3632
0
    return -1;
3633
0
  }
3634
3635
0
  SetPlane(start_y, dst_stride_y, width, height, value_y);
3636
0
  SetPlane(start_u, dst_stride_u, halfwidth, halfheight, value_u);
3637
0
  SetPlane(start_v, dst_stride_v, halfwidth, halfheight, value_v);
3638
0
  return 0;
3639
0
}
3640
3641
// Draw a rectangle into ARGB
3642
LIBYUV_API
3643
int ARGBRect(uint8_t* dst_argb,
3644
             int dst_stride_argb,
3645
             int dst_x,
3646
             int dst_y,
3647
             int width,
3648
             int height,
3649
0
             uint32_t value) {
3650
0
  int y;
3651
0
  void (*ARGBSetRow)(uint8_t* dst_argb, uint32_t value, int width) =
3652
0
      ARGBSetRow_C;
3653
0
  if (!dst_argb || width <= 0 || height == 0 || height == INT_MIN ||
3654
0
      dst_x < 0 || dst_y < 0) {
3655
0
    return -1;
3656
0
  }
3657
0
  if (height < 0) {
3658
0
    height = -height;
3659
0
    dst_argb = dst_argb + (ptrdiff_t)(height - 1) * dst_stride_argb;
3660
0
    dst_stride_argb = -dst_stride_argb;
3661
0
  }
3662
0
  dst_argb += dst_y * dst_stride_argb + dst_x * 4;
3663
  // Coalesce rows.
3664
0
  if (dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
3665
0
    width *= height;
3666
0
    height = 1;
3667
0
    dst_stride_argb = 0;
3668
0
  }
3669
3670
#if defined(HAS_ARGBSETROW_NEON)
3671
  if (TestCpuFlag(kCpuHasNEON)) {
3672
    ARGBSetRow = ARGBSetRow_Any_NEON;
3673
    if (IS_ALIGNED(width, 4)) {
3674
      ARGBSetRow = ARGBSetRow_NEON;
3675
    }
3676
  }
3677
#endif
3678
0
#if defined(HAS_ARGBSETROW_X86)
3679
0
  if (TestCpuFlag(kCpuHasX86)) {
3680
0
    ARGBSetRow = ARGBSetRow_X86;
3681
0
  }
3682
0
#endif
3683
#if defined(HAS_ARGBSETROW_LSX)
3684
  if (TestCpuFlag(kCpuHasLSX)) {
3685
    ARGBSetRow = ARGBSetRow_Any_LSX;
3686
    if (IS_ALIGNED(width, 4)) {
3687
      ARGBSetRow = ARGBSetRow_LSX;
3688
    }
3689
  }
3690
#endif
3691
3692
  // Set plane
3693
0
  for (y = 0; y < height; ++y) {
3694
0
    ARGBSetRow(dst_argb, value, width);
3695
0
    dst_argb += dst_stride_argb;
3696
0
  }
3697
0
  return 0;
3698
0
}
3699
3700
// Convert unattentuated ARGB to preattenuated ARGB.
3701
// An unattenutated ARGB alpha blend uses the formula
3702
// p = a * f + (1 - a) * b
3703
// where
3704
//   p is output pixel
3705
//   f is foreground pixel
3706
//   b is background pixel
3707
//   a is alpha value from foreground pixel
3708
// An preattenutated ARGB alpha blend uses the formula
3709
// p = f + (1 - a) * b
3710
// where
3711
//   f is foreground pixel premultiplied by alpha
3712
3713
LIBYUV_API
3714
int ARGBAttenuate(const uint8_t* src_argb,
3715
                  int src_stride_argb,
3716
                  uint8_t* dst_argb,
3717
                  int dst_stride_argb,
3718
                  int width,
3719
741
                  int height) {
3720
741
  int y;
3721
741
  void (*ARGBAttenuateRow)(const uint8_t* src_argb, uint8_t* dst_argb,
3722
741
                           int width) = ARGBAttenuateRow_C;
3723
741
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
3724
741
      height == INT_MIN) {
3725
0
    return -1;
3726
0
  }
3727
741
  if (height < 0) {
3728
0
    height = -height;
3729
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
3730
0
    src_stride_argb = -src_stride_argb;
3731
0
  }
3732
  // Coalesce rows.
3733
741
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
3734
741
      (ptrdiff_t)width * height <= INT_MAX) {
3735
741
    width *= height;
3736
741
    height = 1;
3737
741
    src_stride_argb = dst_stride_argb = 0;
3738
741
  }
3739
741
#if defined(HAS_ARGBATTENUATEROW_SSSE3)
3740
741
  if (TestCpuFlag(kCpuHasSSSE3)) {
3741
741
    ARGBAttenuateRow = ARGBAttenuateRow_Any_SSSE3;
3742
741
    if (IS_ALIGNED(width, 4)) {
3743
535
      ARGBAttenuateRow = ARGBAttenuateRow_SSSE3;
3744
535
    }
3745
741
  }
3746
741
#endif
3747
741
#if defined(HAS_ARGBATTENUATEROW_AVX2)
3748
741
  if (TestCpuFlag(kCpuHasAVX2)) {
3749
741
    ARGBAttenuateRow = ARGBAttenuateRow_Any_AVX2;
3750
741
    if (IS_ALIGNED(width, 8)) {
3751
405
      ARGBAttenuateRow = ARGBAttenuateRow_AVX2;
3752
405
    }
3753
741
  }
3754
741
#endif
3755
#if defined(HAS_ARGBATTENUATEROW_NEON)
3756
  if (TestCpuFlag(kCpuHasNEON)) {
3757
    ARGBAttenuateRow = ARGBAttenuateRow_Any_NEON;
3758
    if (IS_ALIGNED(width, 8)) {
3759
      ARGBAttenuateRow = ARGBAttenuateRow_NEON;
3760
    }
3761
  }
3762
#endif
3763
#if defined(HAS_ARGBATTENUATEROW_LSX)
3764
  if (TestCpuFlag(kCpuHasLSX)) {
3765
    ARGBAttenuateRow = ARGBAttenuateRow_Any_LSX;
3766
    if (IS_ALIGNED(width, 8)) {
3767
      ARGBAttenuateRow = ARGBAttenuateRow_LSX;
3768
    }
3769
  }
3770
#endif
3771
#if defined(HAS_ARGBATTENUATEROW_LASX)
3772
  if (TestCpuFlag(kCpuHasLASX)) {
3773
    ARGBAttenuateRow = ARGBAttenuateRow_Any_LASX;
3774
    if (IS_ALIGNED(width, 16)) {
3775
      ARGBAttenuateRow = ARGBAttenuateRow_LASX;
3776
    }
3777
  }
3778
#endif
3779
#if defined(HAS_ARGBATTENUATEROW_RVV)
3780
  if (TestCpuFlag(kCpuHasRVV)) {
3781
    ARGBAttenuateRow = ARGBAttenuateRow_RVV;
3782
  }
3783
#endif
3784
3785
1.48k
  for (y = 0; y < height; ++y) {
3786
741
    ARGBAttenuateRow(src_argb, dst_argb, width);
3787
741
    src_argb += src_stride_argb;
3788
741
    dst_argb += dst_stride_argb;
3789
741
  }
3790
741
  return 0;
3791
741
}
3792
3793
// Convert preattentuated ARGB to unattenuated ARGB.
3794
LIBYUV_API
3795
int ARGBUnattenuate(const uint8_t* src_argb,
3796
                    int src_stride_argb,
3797
                    uint8_t* dst_argb,
3798
                    int dst_stride_argb,
3799
                    int width,
3800
0
                    int height) {
3801
0
  int y;
3802
0
  void (*ARGBUnattenuateRow)(const uint8_t* src_argb, uint8_t* dst_argb,
3803
0
                             int width) = ARGBUnattenuateRow_C;
3804
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
3805
0
      height == INT_MIN) {
3806
0
    return -1;
3807
0
  }
3808
0
  if (height < 0) {
3809
0
    height = -height;
3810
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
3811
0
    src_stride_argb = -src_stride_argb;
3812
0
  }
3813
  // Coalesce rows.
3814
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
3815
0
      (ptrdiff_t)width * height <= INT_MAX) {
3816
0
    width *= height;
3817
0
    height = 1;
3818
0
    src_stride_argb = dst_stride_argb = 0;
3819
0
  }
3820
0
#if defined(HAS_ARGBUNATTENUATEROW_SSE2)
3821
0
  if (TestCpuFlag(kCpuHasSSE2)) {
3822
0
    ARGBUnattenuateRow = ARGBUnattenuateRow_Any_SSE2;
3823
0
    if (IS_ALIGNED(width, 4)) {
3824
0
      ARGBUnattenuateRow = ARGBUnattenuateRow_SSE2;
3825
0
    }
3826
0
  }
3827
0
#endif
3828
0
#if defined(HAS_ARGBUNATTENUATEROW_AVX2)
3829
0
  if (TestCpuFlag(kCpuHasAVX2)) {
3830
0
    ARGBUnattenuateRow = ARGBUnattenuateRow_Any_AVX2;
3831
0
    if (IS_ALIGNED(width, 8)) {
3832
0
      ARGBUnattenuateRow = ARGBUnattenuateRow_AVX2;
3833
0
    }
3834
0
  }
3835
0
#endif
3836
  // TODO(fbarchard): Neon version.
3837
3838
0
  for (y = 0; y < height; ++y) {
3839
0
    ARGBUnattenuateRow(src_argb, dst_argb, width);
3840
0
    src_argb += src_stride_argb;
3841
0
    dst_argb += dst_stride_argb;
3842
0
  }
3843
0
  return 0;
3844
0
}
3845
3846
// Convert ARGB to Grayed ARGB.
3847
LIBYUV_API
3848
int ARGBGrayTo(const uint8_t* src_argb,
3849
               int src_stride_argb,
3850
               uint8_t* dst_argb,
3851
               int dst_stride_argb,
3852
               int width,
3853
0
               int height) {
3854
0
  int y;
3855
0
  void (*ARGBGrayRow)(const uint8_t* src_argb, uint8_t* dst_argb, int width) =
3856
0
      ARGBGrayRow_C;
3857
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
3858
0
      height == INT_MIN) {
3859
0
    return -1;
3860
0
  }
3861
0
  if (height < 0) {
3862
0
    height = -height;
3863
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
3864
0
    src_stride_argb = -src_stride_argb;
3865
0
  }
3866
  // Coalesce rows.
3867
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
3868
0
      (ptrdiff_t)width * height <= INT_MAX) {
3869
0
    width *= height;
3870
0
    height = 1;
3871
0
    src_stride_argb = dst_stride_argb = 0;
3872
0
  }
3873
0
#if defined(HAS_ARGBGRAYROW_SSSE3)
3874
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
3875
0
    ARGBGrayRow = ARGBGrayRow_SSSE3;
3876
0
  }
3877
0
#endif
3878
#if defined(HAS_ARGBGRAYROW_NEON)
3879
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
3880
    ARGBGrayRow = ARGBGrayRow_NEON;
3881
  }
3882
#endif
3883
#if defined(HAS_ARGBGRAYROW_NEON_DOTPROD)
3884
  if (TestCpuFlag(kCpuHasNeonDotProd) && IS_ALIGNED(width, 8)) {
3885
    ARGBGrayRow = ARGBGrayRow_NEON_DotProd;
3886
  }
3887
#endif
3888
#if defined(HAS_ARGBGRAYROW_LSX)
3889
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 8)) {
3890
    ARGBGrayRow = ARGBGrayRow_LSX;
3891
  }
3892
#endif
3893
#if defined(HAS_ARGBGRAYROW_LASX)
3894
  if (TestCpuFlag(kCpuHasLASX) && IS_ALIGNED(width, 16)) {
3895
    ARGBGrayRow = ARGBGrayRow_LASX;
3896
  }
3897
#endif
3898
3899
0
  for (y = 0; y < height; ++y) {
3900
0
    ARGBGrayRow(src_argb, dst_argb, width);
3901
0
    src_argb += src_stride_argb;
3902
0
    dst_argb += dst_stride_argb;
3903
0
  }
3904
0
  return 0;
3905
0
}
3906
3907
// Make a rectangle of ARGB gray scale.
3908
LIBYUV_API
3909
int ARGBGray(uint8_t* dst_argb,
3910
             int dst_stride_argb,
3911
             int dst_x,
3912
             int dst_y,
3913
             int width,
3914
0
             int height) {
3915
0
  int y;
3916
0
  void (*ARGBGrayRow)(const uint8_t* src_argb, uint8_t* dst_argb, int width) =
3917
0
      ARGBGrayRow_C;
3918
0
  uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
3919
0
  if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0) {
3920
0
    return -1;
3921
0
  }
3922
  // Coalesce rows.
3923
0
  if (dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
3924
0
    width *= height;
3925
0
    height = 1;
3926
0
    dst_stride_argb = 0;
3927
0
  }
3928
0
#if defined(HAS_ARGBGRAYROW_SSSE3)
3929
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
3930
0
    ARGBGrayRow = ARGBGrayRow_SSSE3;
3931
0
  }
3932
0
#endif
3933
#if defined(HAS_ARGBGRAYROW_NEON)
3934
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
3935
    ARGBGrayRow = ARGBGrayRow_NEON;
3936
  }
3937
#endif
3938
#if defined(HAS_ARGBGRAYROW_NEON_DOTPROD)
3939
  if (TestCpuFlag(kCpuHasNeonDotProd) && IS_ALIGNED(width, 8)) {
3940
    ARGBGrayRow = ARGBGrayRow_NEON_DotProd;
3941
  }
3942
#endif
3943
#if defined(HAS_ARGBGRAYROW_LSX)
3944
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 8)) {
3945
    ARGBGrayRow = ARGBGrayRow_LSX;
3946
  }
3947
#endif
3948
#if defined(HAS_ARGBGRAYROW_LASX)
3949
  if (TestCpuFlag(kCpuHasLASX) && IS_ALIGNED(width, 16)) {
3950
    ARGBGrayRow = ARGBGrayRow_LASX;
3951
  }
3952
#endif
3953
3954
0
  for (y = 0; y < height; ++y) {
3955
0
    ARGBGrayRow(dst, dst, width);
3956
0
    dst += dst_stride_argb;
3957
0
  }
3958
0
  return 0;
3959
0
}
3960
3961
// Make a rectangle of ARGB Sepia tone.
3962
LIBYUV_API
3963
int ARGBSepia(uint8_t* dst_argb,
3964
              int dst_stride_argb,
3965
              int dst_x,
3966
              int dst_y,
3967
              int width,
3968
0
              int height) {
3969
0
  int y;
3970
0
  void (*ARGBSepiaRow)(uint8_t* dst_argb, int width) = ARGBSepiaRow_C;
3971
0
  uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
3972
0
  if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0) {
3973
0
    return -1;
3974
0
  }
3975
  // Coalesce rows.
3976
0
  if (dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
3977
0
    width *= height;
3978
0
    height = 1;
3979
0
    dst_stride_argb = 0;
3980
0
  }
3981
0
#if defined(HAS_ARGBSEPIAROW_SSSE3)
3982
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
3983
0
    ARGBSepiaRow = ARGBSepiaRow_SSSE3;
3984
0
  }
3985
0
#endif
3986
#if defined(HAS_ARGBSEPIAROW_NEON)
3987
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
3988
    ARGBSepiaRow = ARGBSepiaRow_NEON;
3989
  }
3990
#endif
3991
#if defined(HAS_ARGBSEPIAROW_NEON_DOTPROD)
3992
  if (TestCpuFlag(kCpuHasNeonDotProd) && IS_ALIGNED(width, 8)) {
3993
    ARGBSepiaRow = ARGBSepiaRow_NEON_DotProd;
3994
  }
3995
#endif
3996
#if defined(HAS_ARGBSEPIAROW_LSX)
3997
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 8)) {
3998
    ARGBSepiaRow = ARGBSepiaRow_LSX;
3999
  }
4000
#endif
4001
#if defined(HAS_ARGBSEPIAROW_LASX)
4002
  if (TestCpuFlag(kCpuHasLASX) && IS_ALIGNED(width, 16)) {
4003
    ARGBSepiaRow = ARGBSepiaRow_LASX;
4004
  }
4005
#endif
4006
4007
0
  for (y = 0; y < height; ++y) {
4008
0
    ARGBSepiaRow(dst, width);
4009
0
    dst += dst_stride_argb;
4010
0
  }
4011
0
  return 0;
4012
0
}
4013
4014
// Apply a 4x4 matrix to each ARGB pixel.
4015
// Note: Normally for shading, but can be used to swizzle or invert.
4016
LIBYUV_API
4017
int ARGBColorMatrix(const uint8_t* src_argb,
4018
                    int src_stride_argb,
4019
                    uint8_t* dst_argb,
4020
                    int dst_stride_argb,
4021
                    const int8_t* matrix_argb,
4022
                    int width,
4023
0
                    int height) {
4024
0
  int y;
4025
0
  void (*ARGBColorMatrixRow)(const uint8_t* src_argb, uint8_t* dst_argb,
4026
0
                             const int8_t* matrix_argb, int width) =
4027
0
      ARGBColorMatrixRow_C;
4028
0
  if (!src_argb || !dst_argb || !matrix_argb || width <= 0 || height == 0 ||
4029
0
      height == INT_MIN) {
4030
0
    return -1;
4031
0
  }
4032
0
  if (height < 0) {
4033
0
    height = -height;
4034
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
4035
0
    src_stride_argb = -src_stride_argb;
4036
0
  }
4037
  // Coalesce rows.
4038
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
4039
0
      (ptrdiff_t)width * height <= INT_MAX) {
4040
0
    width *= height;
4041
0
    height = 1;
4042
0
    src_stride_argb = dst_stride_argb = 0;
4043
0
  }
4044
0
#if defined(HAS_ARGBCOLORMATRIXROW_SSSE3)
4045
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
4046
0
    ARGBColorMatrixRow = ARGBColorMatrixRow_SSSE3;
4047
0
  }
4048
0
#endif
4049
#if defined(HAS_ARGBCOLORMATRIXROW_NEON)
4050
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
4051
    ARGBColorMatrixRow = ARGBColorMatrixRow_NEON;
4052
  }
4053
#endif
4054
#if defined(HAS_ARGBCOLORMATRIXROW_NEON_I8MM)
4055
  if (TestCpuFlag(kCpuHasNeonI8MM) && IS_ALIGNED(width, 8)) {
4056
    ARGBColorMatrixRow = ARGBColorMatrixRow_NEON_I8MM;
4057
  }
4058
#endif
4059
#if defined(HAS_ARGBCOLORMATRIXROW_LSX)
4060
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 8)) {
4061
    ARGBColorMatrixRow = ARGBColorMatrixRow_LSX;
4062
  }
4063
#endif
4064
0
  for (y = 0; y < height; ++y) {
4065
0
    ARGBColorMatrixRow(src_argb, dst_argb, matrix_argb, width);
4066
0
    src_argb += src_stride_argb;
4067
0
    dst_argb += dst_stride_argb;
4068
0
  }
4069
0
  return 0;
4070
0
}
4071
4072
// Apply a 4x3 matrix to each ARGB pixel.
4073
// Deprecated.
4074
LIBYUV_API
4075
int RGBColorMatrix(uint8_t* dst_argb,
4076
                   int dst_stride_argb,
4077
                   const int8_t* matrix_rgb,
4078
                   int dst_x,
4079
                   int dst_y,
4080
                   int width,
4081
0
                   int height) {
4082
0
  SIMD_ALIGNED(int8_t matrix_argb[16]);
4083
0
  uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
4084
0
  if (!dst_argb || !matrix_rgb || width <= 0 || height <= 0 || dst_x < 0 ||
4085
0
      dst_y < 0) {
4086
0
    return -1;
4087
0
  }
4088
4089
  // Convert 4x3 7 bit matrix to 4x4 6 bit matrix.
4090
0
  matrix_argb[0] = matrix_rgb[0] / 2;
4091
0
  matrix_argb[1] = matrix_rgb[1] / 2;
4092
0
  matrix_argb[2] = matrix_rgb[2] / 2;
4093
0
  matrix_argb[3] = matrix_rgb[3] / 2;
4094
0
  matrix_argb[4] = matrix_rgb[4] / 2;
4095
0
  matrix_argb[5] = matrix_rgb[5] / 2;
4096
0
  matrix_argb[6] = matrix_rgb[6] / 2;
4097
0
  matrix_argb[7] = matrix_rgb[7] / 2;
4098
0
  matrix_argb[8] = matrix_rgb[8] / 2;
4099
0
  matrix_argb[9] = matrix_rgb[9] / 2;
4100
0
  matrix_argb[10] = matrix_rgb[10] / 2;
4101
0
  matrix_argb[11] = matrix_rgb[11] / 2;
4102
0
  matrix_argb[14] = matrix_argb[13] = matrix_argb[12] = 0;
4103
0
  matrix_argb[15] = 64;  // 1.0
4104
4105
0
  return ARGBColorMatrix((const uint8_t*)(dst), dst_stride_argb, dst,
4106
0
                         dst_stride_argb, &matrix_argb[0], width, height);
4107
0
}
4108
4109
// Apply a color table each ARGB pixel.
4110
// Table contains 256 ARGB values.
4111
LIBYUV_API
4112
int ARGBColorTable(uint8_t* dst_argb,
4113
                   int dst_stride_argb,
4114
                   const uint8_t* table_argb,
4115
                   int dst_x,
4116
                   int dst_y,
4117
                   int width,
4118
0
                   int height) {
4119
0
  int y;
4120
0
  void (*ARGBColorTableRow)(uint8_t* dst_argb, const uint8_t* table_argb,
4121
0
                            int width) = ARGBColorTableRow_C;
4122
0
  uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
4123
0
  if (!dst_argb || !table_argb || width <= 0 || height <= 0 || dst_x < 0 ||
4124
0
      dst_y < 0) {
4125
0
    return -1;
4126
0
  }
4127
  // Coalesce rows.
4128
0
  if (dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
4129
0
    width *= height;
4130
0
    height = 1;
4131
0
    dst_stride_argb = 0;
4132
0
  }
4133
0
#if defined(HAS_ARGBCOLORTABLEROW_X86)
4134
0
  if (TestCpuFlag(kCpuHasX86)) {
4135
0
    ARGBColorTableRow = ARGBColorTableRow_X86;
4136
0
  }
4137
0
#endif
4138
0
  for (y = 0; y < height; ++y) {
4139
0
    ARGBColorTableRow(dst, table_argb, width);
4140
0
    dst += dst_stride_argb;
4141
0
  }
4142
0
  return 0;
4143
0
}
4144
4145
// Apply a color table each ARGB pixel but preserve destination alpha.
4146
// Table contains 256 ARGB values.
4147
LIBYUV_API
4148
int RGBColorTable(uint8_t* dst_argb,
4149
                  int dst_stride_argb,
4150
                  const uint8_t* table_argb,
4151
                  int dst_x,
4152
                  int dst_y,
4153
                  int width,
4154
0
                  int height) {
4155
0
  int y;
4156
0
  void (*RGBColorTableRow)(uint8_t* dst_argb, const uint8_t* table_argb,
4157
0
                           int width) = RGBColorTableRow_C;
4158
0
  uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
4159
0
  if (!dst_argb || !table_argb || width <= 0 || height <= 0 || dst_x < 0 ||
4160
0
      dst_y < 0) {
4161
0
    return -1;
4162
0
  }
4163
  // Coalesce rows.
4164
0
  if (dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
4165
0
    width *= height;
4166
0
    height = 1;
4167
0
    dst_stride_argb = 0;
4168
0
  }
4169
0
#if defined(HAS_RGBCOLORTABLEROW_X86)
4170
0
  if (TestCpuFlag(kCpuHasX86)) {
4171
0
    RGBColorTableRow = RGBColorTableRow_X86;
4172
0
  }
4173
0
#endif
4174
0
  for (y = 0; y < height; ++y) {
4175
0
    RGBColorTableRow(dst, table_argb, width);
4176
0
    dst += dst_stride_argb;
4177
0
  }
4178
0
  return 0;
4179
0
}
4180
4181
// ARGBQuantize is used to posterize art.
4182
// e.g. rgb / qvalue * qvalue + qvalue / 2
4183
// But the low levels implement efficiently with 3 parameters, and could be
4184
// used for other high level operations.
4185
// dst_argb[0] = (b * scale >> 16) * interval_size + interval_offset;
4186
// where scale is 1 / interval_size as a fixed point value.
4187
// The divide is replaces with a multiply by reciprocal fixed point multiply.
4188
// Caveat - although SSE2 saturates, the C function does not and should be used
4189
// with care if doing anything but quantization.
4190
LIBYUV_API
4191
int ARGBQuantize(uint8_t* dst_argb,
4192
                 int dst_stride_argb,
4193
                 int scale,
4194
                 int interval_size,
4195
                 int interval_offset,
4196
                 int dst_x,
4197
                 int dst_y,
4198
                 int width,
4199
0
                 int height) {
4200
0
  int y;
4201
0
  void (*ARGBQuantizeRow)(uint8_t* dst_argb, int scale, int interval_size,
4202
0
                          int interval_offset, int width) = ARGBQuantizeRow_C;
4203
0
  uint8_t* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
4204
0
  if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0 ||
4205
0
      interval_size < 1 || interval_size > 255) {
4206
0
    return -1;
4207
0
  }
4208
  // Coalesce rows.
4209
0
  if (dst_stride_argb == width * 4 && (ptrdiff_t)width * height <= INT_MAX) {
4210
0
    width *= height;
4211
0
    height = 1;
4212
0
    dst_stride_argb = 0;
4213
0
  }
4214
0
#if defined(HAS_ARGBQUANTIZEROW_SSE2)
4215
0
  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
4216
0
    ARGBQuantizeRow = ARGBQuantizeRow_SSE2;
4217
0
  }
4218
0
#endif
4219
#if defined(HAS_ARGBQUANTIZEROW_NEON)
4220
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
4221
    ARGBQuantizeRow = ARGBQuantizeRow_NEON;
4222
  }
4223
#endif
4224
#if defined(HAS_ARGBQUANTIZEROW_LSX)
4225
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 8)) {
4226
    ARGBQuantizeRow = ARGBQuantizeRow_LSX;
4227
  }
4228
#endif
4229
0
  for (y = 0; y < height; ++y) {
4230
0
    ARGBQuantizeRow(dst, scale, interval_size, interval_offset, width);
4231
0
    dst += dst_stride_argb;
4232
0
  }
4233
0
  return 0;
4234
0
}
4235
4236
// Computes table of cumulative sum for image where the value is the sum
4237
// of all values above and to the left of the entry. Used by ARGBBlur.
4238
LIBYUV_API
4239
int ARGBComputeCumulativeSum(const uint8_t* src_argb,
4240
                             int src_stride_argb,
4241
                             int32_t* dst_cumsum,
4242
                             int dst_stride32_cumsum,
4243
                             int width,
4244
0
                             int height) {
4245
0
  int y;
4246
0
  void (*ComputeCumulativeSumRow)(const uint8_t* row, int32_t* cumsum,
4247
0
                                  const int32_t* previous_cumsum, int width) =
4248
0
      ComputeCumulativeSumRow_C;
4249
0
  int32_t* previous_cumsum = dst_cumsum;
4250
0
  if (!dst_cumsum || !src_argb || width <= 0 || height <= 0) {
4251
0
    return -1;
4252
0
  }
4253
0
#if defined(HAS_CUMULATIVESUMTOAVERAGEROW_SSE2)
4254
0
  if (TestCpuFlag(kCpuHasSSE2)) {
4255
0
    ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
4256
0
  }
4257
0
#endif
4258
4259
0
  memset(dst_cumsum, 0, width * sizeof(dst_cumsum[0]) * 4);  // 4 int per pixel.
4260
0
  for (y = 0; y < height; ++y) {
4261
0
    ComputeCumulativeSumRow(src_argb, dst_cumsum, previous_cumsum, width);
4262
0
    previous_cumsum = dst_cumsum;
4263
0
    dst_cumsum += dst_stride32_cumsum;
4264
0
    src_argb += src_stride_argb;
4265
0
  }
4266
0
  return 0;
4267
0
}
4268
4269
// Blur ARGB image.
4270
// Caller should allocate CumulativeSum table of width * height * 16 bytes
4271
// aligned to 16 byte boundary. height can be radius * 2 + 2 to save memory
4272
// as the buffer is treated as circular.
4273
LIBYUV_API
4274
int ARGBBlur(const uint8_t* src_argb,
4275
             int src_stride_argb,
4276
             uint8_t* dst_argb,
4277
             int dst_stride_argb,
4278
             int32_t* dst_cumsum,
4279
             int dst_stride32_cumsum,
4280
             int width,
4281
             int height,
4282
0
             int radius) {
4283
0
  int y;
4284
0
  void (*ComputeCumulativeSumRow)(const uint8_t* row, int32_t* cumsum,
4285
0
                                  const int32_t* previous_cumsum, int width) =
4286
0
      ComputeCumulativeSumRow_C;
4287
0
  void (*CumulativeSumToAverageRow)(
4288
0
      const int32_t* topleft, const int32_t* botleft, int width, int area,
4289
0
      uint8_t* dst, int count) = CumulativeSumToAverageRow_C;
4290
0
  int32_t* cumsum_bot_row;
4291
0
  int32_t* max_cumsum_bot_row;
4292
0
  int32_t* cumsum_top_row;
4293
4294
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
4295
0
      height == INT_MIN) {
4296
0
    return -1;
4297
0
  }
4298
0
  if (height < 0) {
4299
0
    height = -height;
4300
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
4301
0
    src_stride_argb = -src_stride_argb;
4302
0
  }
4303
0
  if (radius > height) {
4304
0
    radius = height;
4305
0
  }
4306
0
  if (radius > (width / 2 - 1)) {
4307
0
    radius = width / 2 - 1;
4308
0
  }
4309
0
  if (radius <= 0 || height <= 1) {
4310
0
    return -1;
4311
0
  }
4312
0
#if defined(HAS_CUMULATIVESUMTOAVERAGEROW_SSE2)
4313
0
  if (TestCpuFlag(kCpuHasSSE2)) {
4314
0
    ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
4315
0
    CumulativeSumToAverageRow = CumulativeSumToAverageRow_SSE2;
4316
0
  }
4317
0
#endif
4318
  // Compute enough CumulativeSum for first row to be blurred. After this
4319
  // one row of CumulativeSum is updated at a time.
4320
0
  ARGBComputeCumulativeSum(src_argb, src_stride_argb, dst_cumsum,
4321
0
                           dst_stride32_cumsum, width, radius);
4322
4323
0
  src_argb = src_argb + radius * src_stride_argb;
4324
0
  cumsum_bot_row = &dst_cumsum[(radius - 1) * dst_stride32_cumsum];
4325
4326
0
  max_cumsum_bot_row = &dst_cumsum[(radius * 2 + 2) * dst_stride32_cumsum];
4327
0
  cumsum_top_row = &dst_cumsum[0];
4328
4329
0
  for (y = 0; y < height; ++y) {
4330
0
    int top_y = ((y - radius - 1) >= 0) ? (y - radius - 1) : 0;
4331
0
    int bot_y = ((y + radius) < height) ? (y + radius) : (height - 1);
4332
0
    int area = radius * (bot_y - top_y);
4333
0
    int boxwidth = radius * 4;
4334
0
    int x;
4335
0
    int n;
4336
4337
    // Increment cumsum_top_row pointer with circular buffer wrap around.
4338
0
    if (top_y) {
4339
0
      cumsum_top_row += dst_stride32_cumsum;
4340
0
      if (cumsum_top_row >= max_cumsum_bot_row) {
4341
0
        cumsum_top_row = dst_cumsum;
4342
0
      }
4343
0
    }
4344
    // Increment cumsum_bot_row pointer with circular buffer wrap around and
4345
    // then fill in a row of CumulativeSum.
4346
0
    if ((y + radius) < height) {
4347
0
      const int32_t* prev_cumsum_bot_row = cumsum_bot_row;
4348
0
      cumsum_bot_row += dst_stride32_cumsum;
4349
0
      if (cumsum_bot_row >= max_cumsum_bot_row) {
4350
0
        cumsum_bot_row = dst_cumsum;
4351
0
      }
4352
0
      ComputeCumulativeSumRow(src_argb, cumsum_bot_row, prev_cumsum_bot_row,
4353
0
                              width);
4354
0
      src_argb += src_stride_argb;
4355
0
    }
4356
4357
    // Left clipped.
4358
0
    for (x = 0; x < radius + 1; ++x) {
4359
0
      CumulativeSumToAverageRow(cumsum_top_row, cumsum_bot_row, boxwidth, area,
4360
0
                                &dst_argb[x * 4], 1);
4361
0
      area += (bot_y - top_y);
4362
0
      boxwidth += 4;
4363
0
    }
4364
4365
    // Middle unclipped.
4366
0
    n = (width - 1) - radius - x + 1;
4367
0
    CumulativeSumToAverageRow(cumsum_top_row, cumsum_bot_row, boxwidth, area,
4368
0
                              &dst_argb[x * 4], n);
4369
4370
    // Right clipped.
4371
0
    for (x += n; x <= width - 1; ++x) {
4372
0
      area -= (bot_y - top_y);
4373
0
      boxwidth -= 4;
4374
0
      CumulativeSumToAverageRow(cumsum_top_row + (x - radius - 1) * 4,
4375
0
                                cumsum_bot_row + (x - radius - 1) * 4, boxwidth,
4376
0
                                area, &dst_argb[x * 4], 1);
4377
0
    }
4378
0
    dst_argb += dst_stride_argb;
4379
0
  }
4380
0
  return 0;
4381
0
}
4382
4383
// Multiply ARGB image by a specified ARGB value.
4384
LIBYUV_API
4385
int ARGBShade(const uint8_t* src_argb,
4386
              int src_stride_argb,
4387
              uint8_t* dst_argb,
4388
              int dst_stride_argb,
4389
              int width,
4390
              int height,
4391
0
              uint32_t value) {
4392
0
  int y;
4393
0
  void (*ARGBShadeRow)(const uint8_t* src_argb, uint8_t* dst_argb, int width,
4394
0
                       uint32_t value) = ARGBShadeRow_C;
4395
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
4396
0
      height == INT_MIN || value == 0u) {
4397
0
    return -1;
4398
0
  }
4399
0
  if (height < 0) {
4400
0
    height = -height;
4401
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
4402
0
    src_stride_argb = -src_stride_argb;
4403
0
  }
4404
  // Coalesce rows.
4405
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
4406
0
      (ptrdiff_t)width * height <= INT_MAX) {
4407
0
    width *= height;
4408
0
    height = 1;
4409
0
    src_stride_argb = dst_stride_argb = 0;
4410
0
  }
4411
0
#if defined(HAS_ARGBSHADEROW_SSE2)
4412
0
  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
4413
0
    ARGBShadeRow = ARGBShadeRow_SSE2;
4414
0
  }
4415
0
#endif
4416
#if defined(HAS_ARGBSHADEROW_NEON)
4417
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
4418
    ARGBShadeRow = ARGBShadeRow_NEON;
4419
  }
4420
#endif
4421
#if defined(HAS_ARGBSHADEROW_LSX)
4422
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 4)) {
4423
    ARGBShadeRow = ARGBShadeRow_LSX;
4424
  }
4425
#endif
4426
#if defined(HAS_ARGBSHADEROW_LASX)
4427
  if (TestCpuFlag(kCpuHasLASX) && IS_ALIGNED(width, 8)) {
4428
    ARGBShadeRow = ARGBShadeRow_LASX;
4429
  }
4430
#endif
4431
4432
0
  for (y = 0; y < height; ++y) {
4433
0
    ARGBShadeRow(src_argb, dst_argb, width, value);
4434
0
    src_argb += src_stride_argb;
4435
0
    dst_argb += dst_stride_argb;
4436
0
  }
4437
0
  return 0;
4438
0
}
4439
4440
// Interpolate 2 planes by specified amount (0 to 255).
4441
LIBYUV_API
4442
int InterpolatePlane(const uint8_t* src0,
4443
                     int src_stride0,
4444
                     const uint8_t* src1,
4445
                     int src_stride1,
4446
                     uint8_t* dst,
4447
                     int dst_stride,
4448
                     int width,
4449
                     int height,
4450
0
                     int interpolation) {
4451
0
  int y;
4452
0
  void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr,
4453
0
                         ptrdiff_t src_stride, int dst_width,
4454
0
                         int source_y_fraction) = InterpolateRow_C;
4455
0
  if (!src0 || !src1 || !dst || width <= 0 || height == 0 ||
4456
0
      height == INT_MIN) {
4457
0
    return -1;
4458
0
  }
4459
  // Negative height means invert the image.
4460
0
  if (height < 0) {
4461
0
    height = -height;
4462
0
    dst = dst + (ptrdiff_t)(height - 1) * dst_stride;
4463
0
    dst_stride = -dst_stride;
4464
0
  }
4465
  // Coalesce rows.
4466
0
  if (src_stride0 == width && src_stride1 == width && dst_stride == width &&
4467
0
      (ptrdiff_t)width * height <= INT_MAX) {
4468
0
    width *= height;
4469
0
    height = 1;
4470
0
    src_stride0 = src_stride1 = dst_stride = 0;
4471
0
  }
4472
0
#if defined(HAS_INTERPOLATEROW_AVX2)
4473
0
  if (TestCpuFlag(kCpuHasAVX2)) {
4474
0
    InterpolateRow = InterpolateRow_Any_AVX2;
4475
0
    if (IS_ALIGNED(width, 32)) {
4476
0
      InterpolateRow = InterpolateRow_AVX2;
4477
0
    }
4478
0
  }
4479
0
#endif
4480
#if defined(HAS_INTERPOLATEROW_NEON)
4481
  if (TestCpuFlag(kCpuHasNEON)) {
4482
    InterpolateRow = InterpolateRow_Any_NEON;
4483
    if (IS_ALIGNED(width, 16)) {
4484
      InterpolateRow = InterpolateRow_NEON;
4485
    }
4486
  }
4487
#endif
4488
#if defined(HAS_INTERPOLATEROW_SVE2)
4489
  if (TestCpuFlag(kCpuHasSVE2)) {
4490
    InterpolateRow = InterpolateRow_SVE2;
4491
  }
4492
#endif
4493
#if defined(HAS_INTERPOLATEROW_SME)
4494
  if (TestCpuFlag(kCpuHasSME)) {
4495
    InterpolateRow = InterpolateRow_SME;
4496
  }
4497
#endif
4498
#if defined(HAS_INTERPOLATEROW_LSX)
4499
  if (TestCpuFlag(kCpuHasLSX)) {
4500
    InterpolateRow = InterpolateRow_Any_LSX;
4501
    if (IS_ALIGNED(width, 32)) {
4502
      InterpolateRow = InterpolateRow_LSX;
4503
    }
4504
  }
4505
#endif
4506
#if defined(HAS_INTERPOLATEROW_RVV)
4507
  if (TestCpuFlag(kCpuHasRVV)) {
4508
    InterpolateRow = InterpolateRow_RVV;
4509
  }
4510
#endif
4511
4512
0
  for (y = 0; y < height; ++y) {
4513
0
    InterpolateRow(dst, src0, src1 - src0, width, interpolation);
4514
0
    src0 += src_stride0;
4515
0
    src1 += src_stride1;
4516
0
    dst += dst_stride;
4517
0
  }
4518
0
  return 0;
4519
0
}
4520
4521
// Interpolate 2 planes by specified amount (0 to 255).
4522
LIBYUV_API
4523
int InterpolatePlane_16(const uint16_t* src0,
4524
                        int src_stride0,
4525
                        const uint16_t* src1,
4526
                        int src_stride1,
4527
                        uint16_t* dst,
4528
                        int dst_stride,
4529
                        int width,
4530
                        int height,
4531
0
                        int interpolation) {
4532
0
  int y;
4533
0
  void (*InterpolateRow_16)(uint16_t* dst_ptr, const uint16_t* src_ptr,
4534
0
                            ptrdiff_t src_stride, int dst_width,
4535
0
                            int source_y_fraction) = InterpolateRow_16_C;
4536
0
  if (!src0 || !src1 || !dst || width <= 0 || height == 0 ||
4537
0
      height == INT_MIN) {
4538
0
    return -1;
4539
0
  }
4540
  // Negative height means invert the image.
4541
0
  if (height < 0) {
4542
0
    height = -height;
4543
0
    dst = dst + (ptrdiff_t)(height - 1) * dst_stride;
4544
0
    dst_stride = -dst_stride;
4545
0
  }
4546
  // Coalesce rows.
4547
0
  if (src_stride0 == width && src_stride1 == width && dst_stride == width &&
4548
0
      (ptrdiff_t)width * height <= INT_MAX) {
4549
0
    width *= height;
4550
0
    height = 1;
4551
0
    src_stride0 = src_stride1 = dst_stride = 0;
4552
0
  }
4553
#if defined(HAS_INTERPOLATEROW_16_SSSE3)
4554
  if (TestCpuFlag(kCpuHasSSSE3)) {
4555
    InterpolateRow_16 = InterpolateRow_16_Any_SSSE3;
4556
    if (IS_ALIGNED(width, 16)) {
4557
      InterpolateRow_16 = InterpolateRow_16_SSSE3;
4558
    }
4559
  }
4560
#endif
4561
0
#if defined(HAS_INTERPOLATEROW_16_AVX2)
4562
0
  if (TestCpuFlag(kCpuHasAVX2)) {
4563
0
    InterpolateRow_16 = InterpolateRow_16_Any_AVX2;
4564
0
    if (IS_ALIGNED(width, 32)) {
4565
0
      InterpolateRow_16 = InterpolateRow_16_AVX2;
4566
0
    }
4567
0
  }
4568
0
#endif
4569
#if defined(HAS_INTERPOLATEROW_16_NEON)
4570
  if (TestCpuFlag(kCpuHasNEON)) {
4571
    InterpolateRow_16 = InterpolateRow_16_Any_NEON;
4572
    if (IS_ALIGNED(width, 8)) {
4573
      InterpolateRow_16 = InterpolateRow_16_NEON;
4574
    }
4575
  }
4576
#endif
4577
#if defined(HAS_INTERPOLATEROW_16_SME)
4578
  if (TestCpuFlag(kCpuHasSME)) {
4579
    InterpolateRow_16 = InterpolateRow_16_SME;
4580
  }
4581
#endif
4582
#if defined(HAS_INTERPOLATEROW_16_LSX)
4583
  if (TestCpuFlag(kCpuHasLSX)) {
4584
    InterpolateRow_16 = InterpolateRow_16_Any_LSX;
4585
    if (IS_ALIGNED(width, 32)) {
4586
      InterpolateRow_16 = InterpolateRow_16_LSX;
4587
    }
4588
  }
4589
#endif
4590
4591
0
  for (y = 0; y < height; ++y) {
4592
0
    InterpolateRow_16(dst, src0, src1 - src0, width, interpolation);
4593
0
    src0 += src_stride0;
4594
0
    src1 += src_stride1;
4595
0
    dst += dst_stride;
4596
0
  }
4597
0
  return 0;
4598
0
}
4599
4600
// Interpolate 2 ARGB images by specified amount (0 to 255).
4601
LIBYUV_API
4602
int ARGBInterpolate(const uint8_t* src_argb0,
4603
                    int src_stride_argb0,
4604
                    const uint8_t* src_argb1,
4605
                    int src_stride_argb1,
4606
                    uint8_t* dst_argb,
4607
                    int dst_stride_argb,
4608
                    int width,
4609
                    int height,
4610
0
                    int interpolation) {
4611
0
  return InterpolatePlane(src_argb0, src_stride_argb0, src_argb1,
4612
0
                          src_stride_argb1, dst_argb, dst_stride_argb,
4613
0
                          width * 4, height, interpolation);
4614
0
}
4615
4616
// Interpolate 2 YUV images by specified amount (0 to 255).
4617
LIBYUV_API
4618
int I420Interpolate(const uint8_t* src0_y,
4619
                    int src0_stride_y,
4620
                    const uint8_t* src0_u,
4621
                    int src0_stride_u,
4622
                    const uint8_t* src0_v,
4623
                    int src0_stride_v,
4624
                    const uint8_t* src1_y,
4625
                    int src1_stride_y,
4626
                    const uint8_t* src1_u,
4627
                    int src1_stride_u,
4628
                    const uint8_t* src1_v,
4629
                    int src1_stride_v,
4630
                    uint8_t* dst_y,
4631
                    int dst_stride_y,
4632
                    uint8_t* dst_u,
4633
                    int dst_stride_u,
4634
                    uint8_t* dst_v,
4635
                    int dst_stride_v,
4636
                    int width,
4637
                    int height,
4638
0
                    int interpolation) {
4639
0
  int halfwidth = (width + 1) >> 1;
4640
0
  int halfheight = (height + 1) >> 1;
4641
4642
0
  if (!src0_y || !src0_u || !src0_v || !src1_y || !src1_u || !src1_v ||
4643
0
      !dst_y || !dst_u || !dst_v || width <= 0 || height == 0 ||
4644
0
      height == INT_MIN) {
4645
0
    return -1;
4646
0
  }
4647
4648
0
  InterpolatePlane(src0_y, src0_stride_y, src1_y, src1_stride_y, dst_y,
4649
0
                   dst_stride_y, width, height, interpolation);
4650
0
  InterpolatePlane(src0_u, src0_stride_u, src1_u, src1_stride_u, dst_u,
4651
0
                   dst_stride_u, halfwidth, halfheight, interpolation);
4652
0
  InterpolatePlane(src0_v, src0_stride_v, src1_v, src1_stride_v, dst_v,
4653
0
                   dst_stride_v, halfwidth, halfheight, interpolation);
4654
0
  return 0;
4655
0
}
4656
4657
// Shuffle ARGB channel order.  e.g. BGRA to ARGB.
4658
LIBYUV_API
4659
int ARGBShuffle(const uint8_t* src_argb,
4660
                int src_stride_argb,
4661
                uint8_t* dst_argb,
4662
                int dst_stride_argb,
4663
                const uint8_t* shuffler,
4664
                int width,
4665
0
                int height) {
4666
0
  int y;
4667
0
  void (*ARGBShuffleRow)(const uint8_t* src_argb, uint8_t* dst_argb,
4668
0
                         const uint8_t* shuffler, int width) = ARGBShuffleRow_C;
4669
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
4670
0
      height == INT_MIN) {
4671
0
    return -1;
4672
0
  }
4673
  // Negative height means invert the image.
4674
0
  if (height < 0) {
4675
0
    height = -height;
4676
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
4677
0
    src_stride_argb = -src_stride_argb;
4678
0
  }
4679
  // Coalesce rows.
4680
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
4681
0
      (ptrdiff_t)width * height <= INT_MAX) {
4682
0
    width *= height;
4683
0
    height = 1;
4684
0
    src_stride_argb = dst_stride_argb = 0;
4685
0
  }
4686
0
#if defined(HAS_ARGBSHUFFLEROW_SSSE3)
4687
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
4688
0
    ARGBShuffleRow = ARGBShuffleRow_Any_SSSE3;
4689
0
    if (IS_ALIGNED(width, 8)) {
4690
0
      ARGBShuffleRow = ARGBShuffleRow_SSSE3;
4691
0
    }
4692
0
  }
4693
0
#endif
4694
0
#if defined(HAS_ARGBSHUFFLEROW_AVX2)
4695
0
  if (TestCpuFlag(kCpuHasAVX2)) {
4696
0
    ARGBShuffleRow = ARGBShuffleRow_Any_AVX2;
4697
0
    if (IS_ALIGNED(width, 16)) {
4698
0
      ARGBShuffleRow = ARGBShuffleRow_AVX2;
4699
0
    }
4700
0
  }
4701
0
#endif
4702
0
#if defined(HAS_ARGBSHUFFLEROW_AVX512BW)
4703
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
4704
0
    ARGBShuffleRow = ARGBShuffleRow_Any_AVX512BW;
4705
0
    if (IS_ALIGNED(width, 32)) {
4706
0
      ARGBShuffleRow = ARGBShuffleRow_AVX512BW;
4707
0
    }
4708
0
  }
4709
0
#endif
4710
#if defined(HAS_ARGBSHUFFLEROW_NEON)
4711
  if (TestCpuFlag(kCpuHasNEON)) {
4712
    ARGBShuffleRow = ARGBShuffleRow_Any_NEON;
4713
    if (IS_ALIGNED(width, 4)) {
4714
      ARGBShuffleRow = ARGBShuffleRow_NEON;
4715
    }
4716
  }
4717
#endif
4718
#if defined(HAS_ARGBSHUFFLEROW_LSX)
4719
  if (TestCpuFlag(kCpuHasLSX)) {
4720
    ARGBShuffleRow = ARGBShuffleRow_Any_LSX;
4721
    if (IS_ALIGNED(width, 8)) {
4722
      ARGBShuffleRow = ARGBShuffleRow_LSX;
4723
    }
4724
  }
4725
#endif
4726
#if defined(HAS_ARGBSHUFFLEROW_LASX)
4727
  if (TestCpuFlag(kCpuHasLASX)) {
4728
    ARGBShuffleRow = ARGBShuffleRow_Any_LASX;
4729
    if (IS_ALIGNED(width, 16)) {
4730
      ARGBShuffleRow = ARGBShuffleRow_LASX;
4731
    }
4732
  }
4733
#endif
4734
4735
0
  for (y = 0; y < height; ++y) {
4736
0
    ARGBShuffleRow(src_argb, dst_argb, shuffler, width);
4737
0
    src_argb += src_stride_argb;
4738
0
    dst_argb += dst_stride_argb;
4739
0
  }
4740
0
  return 0;
4741
0
}
4742
4743
// Shuffle AR64 channel order.  e.g. AR64 to AB64.
4744
LIBYUV_API
4745
int AR64Shuffle(const uint16_t* src_ar64,
4746
                int src_stride_ar64,
4747
                uint16_t* dst_ar64,
4748
                int dst_stride_ar64,
4749
                const uint8_t* shuffler,
4750
                int width,
4751
0
                int height) {
4752
0
  int y;
4753
0
  void (*AR64ShuffleRow)(const uint8_t* src_ar64, uint8_t* dst_ar64,
4754
0
                         const uint8_t* shuffler, int width) = AR64ShuffleRow_C;
4755
0
  if (!src_ar64 || !dst_ar64 || width <= 0 || height == 0 ||
4756
0
      height == INT_MIN) {
4757
0
    return -1;
4758
0
  }
4759
  // Negative height means invert the image.
4760
0
  if (height < 0) {
4761
0
    height = -height;
4762
0
    src_ar64 = src_ar64 + (ptrdiff_t)(height - 1) * src_stride_ar64;
4763
0
    src_stride_ar64 = -src_stride_ar64;
4764
0
  }
4765
  // Coalesce rows.
4766
0
  if (src_stride_ar64 == width * 4 && dst_stride_ar64 == width * 4 &&
4767
0
      (ptrdiff_t)width * height <= INT_MAX) {
4768
0
    width *= height;
4769
0
    height = 1;
4770
0
    src_stride_ar64 = dst_stride_ar64 = 0;
4771
0
  }
4772
  // Assembly versions can be reused if it's implemented with shuffle.
4773
0
#if defined(HAS_ARGBSHUFFLEROW_SSSE3)
4774
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
4775
0
    AR64ShuffleRow = ARGBShuffleRow_Any_SSSE3;
4776
0
    if (IS_ALIGNED(width, 8)) {
4777
0
      AR64ShuffleRow = ARGBShuffleRow_SSSE3;
4778
0
    }
4779
0
  }
4780
0
#endif
4781
0
#if defined(HAS_ARGBSHUFFLEROW_AVX2)
4782
0
  if (TestCpuFlag(kCpuHasAVX2)) {
4783
0
    AR64ShuffleRow = ARGBShuffleRow_Any_AVX2;
4784
0
    if (IS_ALIGNED(width, 16)) {
4785
0
      AR64ShuffleRow = ARGBShuffleRow_AVX2;
4786
0
    }
4787
0
  }
4788
0
#endif
4789
0
#if defined(HAS_ARGBSHUFFLEROW_AVX512BW)
4790
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
4791
0
    AR64ShuffleRow = ARGBShuffleRow_Any_AVX512BW;
4792
0
    if (IS_ALIGNED(width, 16)) {
4793
0
      AR64ShuffleRow = ARGBShuffleRow_AVX512BW;
4794
0
    }
4795
0
  }
4796
0
#endif
4797
#if defined(HAS_ARGBSHUFFLEROW_NEON)
4798
  if (TestCpuFlag(kCpuHasNEON)) {
4799
    AR64ShuffleRow = ARGBShuffleRow_Any_NEON;
4800
    if (IS_ALIGNED(width, 4)) {
4801
      AR64ShuffleRow = ARGBShuffleRow_NEON;
4802
    }
4803
  }
4804
#endif
4805
4806
0
  for (y = 0; y < height; ++y) {
4807
0
    AR64ShuffleRow((uint8_t*)(src_ar64), (uint8_t*)(dst_ar64), shuffler,
4808
0
                   width * 2);
4809
0
    src_ar64 += src_stride_ar64;
4810
0
    dst_ar64 += dst_stride_ar64;
4811
0
  }
4812
0
  return 0;
4813
0
}
4814
4815
// Gauss blur a float plane using Gaussian 5x5 filter with
4816
// coefficients of 1, 4, 6, 4, 1.
4817
// Each destination pixel is a blur of the 5x5
4818
// pixels from the source.
4819
// Source edges are clamped.
4820
// Edge is 2 pixels on each side, and interior is multiple of 4.
4821
LIBYUV_API
4822
int GaussPlane_F32(const float* src,
4823
                   int src_stride,
4824
                   float* dst,
4825
                   int dst_stride,
4826
                   int width,
4827
0
                   int height) {
4828
0
  int y;
4829
0
  void (*GaussCol_F32)(const float* src0, const float* src1, const float* src2,
4830
0
                       const float* src3, const float* src4, float* dst,
4831
0
                       int width) = GaussCol_F32_C;
4832
0
  void (*GaussRow_F32)(const float* src, float* dst, int width) =
4833
0
      GaussRow_F32_C;
4834
0
  if (!src || !dst || width <= 0 || height == 0 || height == INT_MIN) {
4835
0
    return -1;
4836
0
  }
4837
  // Negative height means invert the image.
4838
0
  if (height < 0) {
4839
0
    height = -height;
4840
0
    src = src + (ptrdiff_t)(height - 1) * src_stride;
4841
0
    src_stride = -src_stride;
4842
0
  }
4843
4844
#if defined(HAS_GAUSSCOL_F32_NEON)
4845
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
4846
    GaussCol_F32 = GaussCol_F32_NEON;
4847
  }
4848
#endif
4849
#if defined(HAS_GAUSSROW_F32_NEON)
4850
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
4851
    GaussRow_F32 = GaussRow_F32_NEON;
4852
  }
4853
#endif
4854
0
  {
4855
    // 2 pixels on each side, but aligned out to 16 bytes.
4856
0
    align_buffer_64(rowbuf, (4 + width + 4) * 4);
4857
0
    if (!rowbuf)
4858
0
      return 1;
4859
0
    memset(rowbuf, 0, 16);
4860
0
    memset(rowbuf + (4 + width) * 4, 0, 16);
4861
0
    float* row = (float*)(rowbuf + 16);
4862
0
    const float* src0 = src;
4863
0
    const float* src1 = src;
4864
0
    const float* src2 = src;
4865
0
    const float* src3 = src2 + ((height > 1) ? src_stride : 0);
4866
0
    const float* src4 = src3 + ((height > 2) ? src_stride : 0);
4867
4868
0
    for (y = 0; y < height; ++y) {
4869
0
      GaussCol_F32(src0, src1, src2, src3, src4, row, width);
4870
4871
      // Extrude edge by 2 floats
4872
0
      row[-2] = row[-1] = row[0];
4873
0
      row[width + 1] = row[width] = row[width - 1];
4874
4875
0
      GaussRow_F32(row - 2, dst, width);
4876
4877
0
      src0 = src1;
4878
0
      src1 = src2;
4879
0
      src2 = src3;
4880
0
      src3 = src4;
4881
0
      if ((y + 2) < (height - 1)) {
4882
0
        src4 += src_stride;
4883
0
      }
4884
0
      dst += dst_stride;
4885
0
    }
4886
0
    free_aligned_buffer_64(rowbuf);
4887
0
  }
4888
0
  return 0;
4889
0
}
4890
4891
// Sobel ARGB effect.
4892
static int ARGBSobelize(const uint8_t* src_argb,
4893
                        int src_stride_argb,
4894
                        uint8_t* dst_argb,
4895
                        int dst_stride_argb,
4896
                        int width,
4897
                        int height,
4898
                        void (*SobelRow)(const uint8_t* src_sobelx,
4899
                                         const uint8_t* src_sobely,
4900
                                         uint8_t* dst,
4901
0
                                         int width)) {
4902
0
  int y;
4903
0
  void (*ARGBToYMatrixRow)(const uint8_t* src_argb, uint8_t* dst_g, int width,
4904
0
                           const struct ArgbConstants* c) = ARGBToYMatrixRow_C;
4905
0
  void (*SobelYRow)(const uint8_t* src_y0, const uint8_t* src_y1,
4906
0
                    uint8_t* dst_sobely, int width) = SobelYRow_C;
4907
0
  void (*SobelXRow)(const uint8_t* src_y0, const uint8_t* src_y1,
4908
0
                    const uint8_t* src_y2, uint8_t* dst_sobely, int width) =
4909
0
      SobelXRow_C;
4910
0
  const int kEdge = 16;  // Extra pixels at start of row for extrude/align.
4911
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
4912
0
      height == INT_MIN) {
4913
0
    return -1;
4914
0
  }
4915
  // Negative height means invert the image.
4916
0
  if (height < 0) {
4917
0
    height = -height;
4918
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
4919
0
    src_stride_argb = -src_stride_argb;
4920
0
  }
4921
4922
0
#if defined(HAS_ARGBTOYMATRIXROW_SSSE3)
4923
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
4924
0
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_SSSE3;
4925
0
    if (IS_ALIGNED(width, 16)) {
4926
0
      ARGBToYMatrixRow = ARGBToYMatrixRow_SSSE3;
4927
0
    }
4928
0
  }
4929
0
#endif
4930
0
#if defined(HAS_ARGBTOYMATRIXROW_AVX2)
4931
0
  if (TestCpuFlag(kCpuHasAVX2)) {
4932
0
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_AVX2;
4933
0
    if (IS_ALIGNED(width, 32)) {
4934
0
      ARGBToYMatrixRow = ARGBToYMatrixRow_AVX2;
4935
0
    }
4936
0
  }
4937
0
#endif
4938
0
#if defined(HAS_ARGBTOYMATRIXROW_AVX512BW)
4939
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
4940
0
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_AVX512BW;
4941
0
    if (IS_ALIGNED(width, 64)) {
4942
0
      ARGBToYMatrixRow = ARGBToYMatrixRow_AVX512BW;
4943
0
    }
4944
0
  }
4945
0
#endif
4946
#if defined(HAS_ARGBTOYMATRIXROW_NEON)
4947
  if (TestCpuFlag(kCpuHasNEON)) {
4948
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_NEON;
4949
    if (IS_ALIGNED(width, 16)) {
4950
      ARGBToYMatrixRow = ARGBToYMatrixRow_NEON;
4951
    }
4952
  }
4953
#endif
4954
#if defined(HAS_ARGBTOYMATRIXROW_NEON_DOTPROD)
4955
  if (TestCpuFlag(kCpuHasNeonDotProd)) {
4956
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_NEON_DotProd;
4957
    if (IS_ALIGNED(width, 16)) {
4958
      ARGBToYMatrixRow = ARGBToYMatrixRow_NEON_DotProd;
4959
    }
4960
  }
4961
#endif
4962
#if defined(HAS_ARGBTOYMATRIXROW_SVE2)
4963
  if (TestCpuFlag(kCpuHasSVE2)) {
4964
    ARGBToYMatrixRow = ARGBToYMatrixRow_SVE2;
4965
  }
4966
#endif
4967
#if defined(HAS_ARGBTOYMATRIXROW_LSX)
4968
  if (TestCpuFlag(kCpuHasLSX)) {
4969
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_LSX;
4970
    if (IS_ALIGNED(width, 16)) {
4971
      ARGBToYMatrixRow = ARGBToYMatrixRow_LSX;
4972
    }
4973
  }
4974
#endif
4975
#if defined(HAS_ARGBTOYMATRIXROW_LASX)
4976
  if (TestCpuFlag(kCpuHasLASX)) {
4977
    ARGBToYMatrixRow = ARGBToYMatrixRow_Any_LASX;
4978
    if (IS_ALIGNED(width, 32)) {
4979
      ARGBToYMatrixRow = ARGBToYMatrixRow_LASX;
4980
    }
4981
  }
4982
#endif
4983
#if defined(HAS_ARGBTOYMATRIXROW_RVV)
4984
  if (TestCpuFlag(kCpuHasRVV)) {
4985
    ARGBToYMatrixRow = ARGBToYMatrixRow_RVV;
4986
  }
4987
#endif
4988
4989
0
#if defined(HAS_SOBELYROW_SSE2)
4990
0
  if (TestCpuFlag(kCpuHasSSE2)) {
4991
0
    SobelYRow = SobelYRow_SSE2;
4992
0
  }
4993
0
#endif
4994
#if defined(HAS_SOBELYROW_NEON)
4995
  if (TestCpuFlag(kCpuHasNEON)) {
4996
    SobelYRow = SobelYRow_NEON;
4997
  }
4998
#endif
4999
0
#if defined(HAS_SOBELXROW_SSE2)
5000
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5001
0
    SobelXRow = SobelXRow_SSE2;
5002
0
  }
5003
0
#endif
5004
#if defined(HAS_SOBELXROW_NEON)
5005
  if (TestCpuFlag(kCpuHasNEON)) {
5006
    SobelXRow = SobelXRow_NEON;
5007
  }
5008
#endif
5009
0
  {
5010
    // 3 rows with edges before/after.
5011
0
    const int row_size = (width + kEdge + 31) & ~31;
5012
0
    align_buffer_64(rows, row_size * 2 + (kEdge + row_size * 3 + kEdge));
5013
0
    uint8_t* row_sobelx = rows;
5014
0
    uint8_t* row_sobely = rows + row_size;
5015
0
    uint8_t* row_y = rows + row_size * 2;
5016
5017
    // Convert first row.
5018
0
    uint8_t* row_y0 = row_y + kEdge;
5019
0
    uint8_t* row_y1 = row_y0 + row_size;
5020
0
    uint8_t* row_y2 = row_y1 + row_size;
5021
0
    if (!rows)
5022
0
      return 1;
5023
0
    ARGBToYMatrixRow(src_argb, row_y0, width, &kArgbJPEGConstants);
5024
0
    row_y0[-1] = row_y0[0];
5025
0
    memset(row_y0 + width, row_y0[width - 1], 16);  // Extrude 16 for valgrind.
5026
0
    ARGBToYMatrixRow(src_argb, row_y1, width, &kArgbJPEGConstants);
5027
0
    row_y1[-1] = row_y1[0];
5028
0
    memset(row_y1 + width, row_y1[width - 1], 16);
5029
0
    memset(row_y2 + width, 0, 16);
5030
5031
0
    for (y = 0; y < height; ++y) {
5032
      // Convert next row of ARGB to G.
5033
0
      if (y < (height - 1)) {
5034
0
        src_argb += src_stride_argb;
5035
0
      }
5036
0
      ARGBToYMatrixRow(src_argb, row_y2, width, &kArgbJPEGConstants);
5037
0
      row_y2[-1] = row_y2[0];
5038
0
      row_y2[width] = row_y2[width - 1];
5039
5040
0
      SobelXRow(row_y0 - 1, row_y1 - 1, row_y2 - 1, row_sobelx, width);
5041
0
      SobelYRow(row_y0 - 1, row_y2 - 1, row_sobely, width);
5042
0
      SobelRow(row_sobelx, row_sobely, dst_argb, width);
5043
5044
      // Cycle thru circular queue of 3 row_y buffers.
5045
0
      {
5046
0
        uint8_t* row_yt = row_y0;
5047
0
        row_y0 = row_y1;
5048
0
        row_y1 = row_y2;
5049
0
        row_y2 = row_yt;
5050
0
      }
5051
5052
0
      dst_argb += dst_stride_argb;
5053
0
    }
5054
0
    free_aligned_buffer_64(rows);
5055
0
  }
5056
0
  return 0;
5057
0
}
5058
5059
// Sobel ARGB effect.
5060
LIBYUV_API
5061
int ARGBSobel(const uint8_t* src_argb,
5062
              int src_stride_argb,
5063
              uint8_t* dst_argb,
5064
              int dst_stride_argb,
5065
              int width,
5066
0
              int height) {
5067
0
  void (*SobelRow)(const uint8_t* src_sobelx, const uint8_t* src_sobely,
5068
0
                   uint8_t* dst_argb, int width) = SobelRow_C;
5069
0
#if defined(HAS_SOBELROW_SSE2)
5070
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5071
0
    SobelRow = SobelRow_Any_SSE2;
5072
0
    if (IS_ALIGNED(width, 16)) {
5073
0
      SobelRow = SobelRow_SSE2;
5074
0
    }
5075
0
  }
5076
0
#endif
5077
#if defined(HAS_SOBELROW_NEON)
5078
  if (TestCpuFlag(kCpuHasNEON)) {
5079
    SobelRow = SobelRow_Any_NEON;
5080
    if (IS_ALIGNED(width, 8)) {
5081
      SobelRow = SobelRow_NEON;
5082
    }
5083
  }
5084
#endif
5085
#if defined(HAS_SOBELROW_LSX)
5086
  if (TestCpuFlag(kCpuHasLSX)) {
5087
    SobelRow = SobelRow_Any_LSX;
5088
    if (IS_ALIGNED(width, 16)) {
5089
      SobelRow = SobelRow_LSX;
5090
    }
5091
  }
5092
#endif
5093
0
  return ARGBSobelize(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
5094
0
                      width, height, SobelRow);
5095
0
}
5096
5097
// Sobel ARGB effect with planar output.
5098
LIBYUV_API
5099
int ARGBSobelToPlane(const uint8_t* src_argb,
5100
                     int src_stride_argb,
5101
                     uint8_t* dst_y,
5102
                     int dst_stride_y,
5103
                     int width,
5104
0
                     int height) {
5105
0
  void (*SobelToPlaneRow)(const uint8_t* src_sobelx, const uint8_t* src_sobely,
5106
0
                          uint8_t* dst_, int width) = SobelToPlaneRow_C;
5107
0
#if defined(HAS_SOBELTOPLANEROW_SSE2)
5108
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5109
0
    SobelToPlaneRow = SobelToPlaneRow_Any_SSE2;
5110
0
    if (IS_ALIGNED(width, 16)) {
5111
0
      SobelToPlaneRow = SobelToPlaneRow_SSE2;
5112
0
    }
5113
0
  }
5114
0
#endif
5115
#if defined(HAS_SOBELTOPLANEROW_NEON)
5116
  if (TestCpuFlag(kCpuHasNEON)) {
5117
    SobelToPlaneRow = SobelToPlaneRow_Any_NEON;
5118
    if (IS_ALIGNED(width, 16)) {
5119
      SobelToPlaneRow = SobelToPlaneRow_NEON;
5120
    }
5121
  }
5122
#endif
5123
#if defined(HAS_SOBELTOPLANEROW_LSX)
5124
  if (TestCpuFlag(kCpuHasLSX)) {
5125
    SobelToPlaneRow = SobelToPlaneRow_Any_LSX;
5126
    if (IS_ALIGNED(width, 32)) {
5127
      SobelToPlaneRow = SobelToPlaneRow_LSX;
5128
    }
5129
  }
5130
#endif
5131
0
  return ARGBSobelize(src_argb, src_stride_argb, dst_y, dst_stride_y, width,
5132
0
                      height, SobelToPlaneRow);
5133
0
}
5134
5135
// SobelXY ARGB effect.
5136
// Similar to Sobel, but also stores Sobel X in R and Sobel Y in B.  G = Sobel.
5137
LIBYUV_API
5138
int ARGBSobelXY(const uint8_t* src_argb,
5139
                int src_stride_argb,
5140
                uint8_t* dst_argb,
5141
                int dst_stride_argb,
5142
                int width,
5143
0
                int height) {
5144
0
  void (*SobelXYRow)(const uint8_t* src_sobelx, const uint8_t* src_sobely,
5145
0
                     uint8_t* dst_argb, int width) = SobelXYRow_C;
5146
0
#if defined(HAS_SOBELXYROW_SSE2)
5147
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5148
0
    SobelXYRow = SobelXYRow_Any_SSE2;
5149
0
    if (IS_ALIGNED(width, 16)) {
5150
0
      SobelXYRow = SobelXYRow_SSE2;
5151
0
    }
5152
0
  }
5153
0
#endif
5154
#if defined(HAS_SOBELXYROW_NEON)
5155
  if (TestCpuFlag(kCpuHasNEON)) {
5156
    SobelXYRow = SobelXYRow_Any_NEON;
5157
    if (IS_ALIGNED(width, 8)) {
5158
      SobelXYRow = SobelXYRow_NEON;
5159
    }
5160
  }
5161
#endif
5162
#if defined(HAS_SOBELXYROW_LSX)
5163
  if (TestCpuFlag(kCpuHasLSX)) {
5164
    SobelXYRow = SobelXYRow_Any_LSX;
5165
    if (IS_ALIGNED(width, 16)) {
5166
      SobelXYRow = SobelXYRow_LSX;
5167
    }
5168
  }
5169
#endif
5170
0
  return ARGBSobelize(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
5171
0
                      width, height, SobelXYRow);
5172
0
}
5173
5174
// Apply a 4x4 polynomial to each ARGB pixel.
5175
LIBYUV_API
5176
int ARGBPolynomial(const uint8_t* src_argb,
5177
                   int src_stride_argb,
5178
                   uint8_t* dst_argb,
5179
                   int dst_stride_argb,
5180
                   const float* poly,
5181
                   int width,
5182
0
                   int height) {
5183
0
  int y;
5184
0
  void (*ARGBPolynomialRow)(const uint8_t* src_argb, uint8_t* dst_argb,
5185
0
                            const float* poly, int width) = ARGBPolynomialRow_C;
5186
0
  if (!src_argb || !dst_argb || !poly || width <= 0 || height == 0 ||
5187
0
      height == INT_MIN) {
5188
0
    return -1;
5189
0
  }
5190
  // Negative height means invert the image.
5191
0
  if (height < 0) {
5192
0
    height = -height;
5193
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
5194
0
    src_stride_argb = -src_stride_argb;
5195
0
  }
5196
  // Coalesce rows.
5197
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
5198
0
      (ptrdiff_t)width * height <= INT_MAX) {
5199
0
    width *= height;
5200
0
    height = 1;
5201
0
    src_stride_argb = dst_stride_argb = 0;
5202
0
  }
5203
0
#if defined(HAS_ARGBPOLYNOMIALROW_SSE2)
5204
0
  if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 2)) {
5205
0
    ARGBPolynomialRow = ARGBPolynomialRow_SSE2;
5206
0
  }
5207
0
#endif
5208
0
#if defined(HAS_ARGBPOLYNOMIALROW_AVX2)
5209
0
  if (TestCpuFlag(kCpuHasAVX2) && TestCpuFlag(kCpuHasFMA3) &&
5210
0
      IS_ALIGNED(width, 2)) {
5211
0
    ARGBPolynomialRow = ARGBPolynomialRow_AVX2;
5212
0
  }
5213
0
#endif
5214
5215
0
  for (y = 0; y < height; ++y) {
5216
0
    ARGBPolynomialRow(src_argb, dst_argb, poly, width);
5217
0
    src_argb += src_stride_argb;
5218
0
    dst_argb += dst_stride_argb;
5219
0
  }
5220
0
  return 0;
5221
0
}
5222
5223
// Convert plane of 16 bit shorts to half floats.
5224
// Source values are multiplied by scale before storing as half float.
5225
LIBYUV_API
5226
int HalfFloatPlane(const uint16_t* src_y,
5227
                   int src_stride_y,
5228
                   uint16_t* dst_y,
5229
                   int dst_stride_y,
5230
                   float scale,
5231
                   int width,
5232
299
                   int height) {
5233
299
  int y;
5234
299
  void (*HalfFloatRow)(const uint16_t* src, uint16_t* dst, float scale,
5235
299
                       int width) = HalfFloatRow_C;
5236
299
  if (!src_y || !dst_y || width <= 0 || height == 0 || height == INT_MIN) {
5237
0
    return -1;
5238
0
  }
5239
299
  src_stride_y >>= 1;
5240
299
  dst_stride_y >>= 1;
5241
  // Negative height means invert the image.
5242
299
  if (height < 0) {
5243
0
    height = -height;
5244
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
5245
0
    src_stride_y = -src_stride_y;
5246
0
  }
5247
  // Coalesce rows.
5248
299
  if (src_stride_y == width && dst_stride_y == width &&
5249
299
      (ptrdiff_t)width * height <= INT_MAX) {
5250
299
    width *= height;
5251
299
    height = 1;
5252
299
    src_stride_y = dst_stride_y = 0;
5253
299
  }
5254
299
#if defined(HAS_HALFFLOATROW_AVX2)
5255
299
  if (TestCpuFlag(kCpuHasAVX2)) {
5256
299
    HalfFloatRow = HalfFloatRow_Any_AVX2;
5257
299
    if (IS_ALIGNED(width, 16)) {
5258
59
      HalfFloatRow = HalfFloatRow_AVX2;
5259
59
    }
5260
299
  }
5261
299
#endif
5262
#if defined(HAS_HALFFLOATROW_F16C)
5263
  if (TestCpuFlag(kCpuHasAVX2) && TestCpuFlag(kCpuHasF16C)) {
5264
    HalfFloatRow =
5265
        (scale == 1.0f) ? HalfFloat1Row_Any_F16C : HalfFloatRow_Any_F16C;
5266
    if (IS_ALIGNED(width, 16)) {
5267
      HalfFloatRow = (scale == 1.0f) ? HalfFloat1Row_F16C : HalfFloatRow_F16C;
5268
    }
5269
  }
5270
#endif
5271
#if defined(HAS_HALFFLOATROW_NEON)
5272
  if (TestCpuFlag(kCpuHasNEON)
5273
#if defined(__arm__)
5274
      // When scale is 1/65535 the scale * 2^-112 used to convert is a denormal.
5275
      // But when Neon vmul is asked to multiply a normal float by that
5276
      // denormal scale, even though the result would have been normal, it
5277
      // flushes to zero.  The scalar version of vmul supports denormals.
5278
      && scale >= 1.0f / 4096.0f
5279
#endif
5280
  ) {
5281
    HalfFloatRow = HalfFloatRow_Any_NEON;
5282
    if (IS_ALIGNED(width, 16)) {
5283
      HalfFloatRow = HalfFloatRow_NEON;
5284
    }
5285
  }
5286
#endif
5287
#if defined(HAS_HALFFLOATROW_SVE2)
5288
  if (TestCpuFlag(kCpuHasSVE2)) {
5289
    HalfFloatRow = scale == 1.0f ? HalfFloat1Row_SVE2 : HalfFloatRow_SVE2;
5290
  }
5291
#endif
5292
#if defined(HAS_HALFFLOATROW_LSX)
5293
  if (TestCpuFlag(kCpuHasLSX)) {
5294
    HalfFloatRow = HalfFloatRow_Any_LSX;
5295
    if (IS_ALIGNED(width, 32)) {
5296
      HalfFloatRow = HalfFloatRow_LSX;
5297
    }
5298
  }
5299
#endif
5300
5301
598
  for (y = 0; y < height; ++y) {
5302
299
    HalfFloatRow(src_y, dst_y, scale, width);
5303
299
    src_y += src_stride_y;
5304
299
    dst_y += dst_stride_y;
5305
299
  }
5306
299
  return 0;
5307
299
}
5308
5309
// Convert a buffer of bytes to floats, scale the values and store as floats.
5310
LIBYUV_API
5311
0
int ByteToFloat(const uint8_t* src_y, float* dst_y, float scale, int width) {
5312
0
  void (*ByteToFloatRow)(const uint8_t* src, float* dst, float scale,
5313
0
                         int width) = ByteToFloatRow_C;
5314
0
  if (!src_y || !dst_y || width <= 0) {
5315
0
    return -1;
5316
0
  }
5317
#if defined(HAS_BYTETOFLOATROW_NEON)
5318
  if (TestCpuFlag(kCpuHasNEON)) {
5319
    ByteToFloatRow = ByteToFloatRow_Any_NEON;
5320
    if (IS_ALIGNED(width, 8)) {
5321
      ByteToFloatRow = ByteToFloatRow_NEON;
5322
    }
5323
  }
5324
#endif
5325
5326
0
  ByteToFloatRow(src_y, dst_y, scale, width);
5327
0
  return 0;
5328
0
}
5329
5330
// Apply a lumacolortable to each ARGB pixel.
5331
LIBYUV_API
5332
int ARGBLumaColorTable(const uint8_t* src_argb,
5333
                       int src_stride_argb,
5334
                       uint8_t* dst_argb,
5335
                       int dst_stride_argb,
5336
                       const uint8_t* luma,
5337
                       int width,
5338
0
                       int height) {
5339
0
  int y;
5340
0
  void (*ARGBLumaColorTableRow)(
5341
0
      const uint8_t* src_argb, uint8_t* dst_argb, int width,
5342
0
      const uint8_t* luma, const uint32_t lumacoeff) = ARGBLumaColorTableRow_C;
5343
0
  if (!src_argb || !dst_argb || !luma || width <= 0 || height == 0 ||
5344
0
      height == INT_MIN) {
5345
0
    return -1;
5346
0
  }
5347
  // Negative height means invert the image.
5348
0
  if (height < 0) {
5349
0
    height = -height;
5350
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
5351
0
    src_stride_argb = -src_stride_argb;
5352
0
  }
5353
  // Coalesce rows.
5354
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
5355
0
      (ptrdiff_t)width * height <= INT_MAX) {
5356
0
    width *= height;
5357
0
    height = 1;
5358
0
    src_stride_argb = dst_stride_argb = 0;
5359
0
  }
5360
0
#if defined(HAS_ARGBLUMACOLORTABLEROW_SSSE3)
5361
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4)) {
5362
0
    ARGBLumaColorTableRow = ARGBLumaColorTableRow_SSSE3;
5363
0
  }
5364
0
#endif
5365
5366
0
  for (y = 0; y < height; ++y) {
5367
0
    ARGBLumaColorTableRow(src_argb, dst_argb, width, luma, 0x00264b0f);
5368
0
    src_argb += src_stride_argb;
5369
0
    dst_argb += dst_stride_argb;
5370
0
  }
5371
0
  return 0;
5372
0
}
5373
5374
// Copy Alpha from one ARGB image to another.
5375
LIBYUV_API
5376
int ARGBCopyAlpha(const uint8_t* src_argb,
5377
                  int src_stride_argb,
5378
                  uint8_t* dst_argb,
5379
                  int dst_stride_argb,
5380
                  int width,
5381
0
                  int height) {
5382
0
  int y;
5383
0
  void (*ARGBCopyAlphaRow)(const uint8_t* src_argb, uint8_t* dst_argb,
5384
0
                           int width) = ARGBCopyAlphaRow_C;
5385
0
  if (!src_argb || !dst_argb || width <= 0 || height == 0 ||
5386
0
      height == INT_MIN) {
5387
0
    return -1;
5388
0
  }
5389
  // Negative height means invert the image.
5390
0
  if (height < 0) {
5391
0
    height = -height;
5392
0
    src_argb = src_argb + (ptrdiff_t)(height - 1) * src_stride_argb;
5393
0
    src_stride_argb = -src_stride_argb;
5394
0
  }
5395
  // Coalesce rows.
5396
0
  if (src_stride_argb == width * 4 && dst_stride_argb == width * 4 &&
5397
0
      (ptrdiff_t)width * height <= INT_MAX) {
5398
0
    width *= height;
5399
0
    height = 1;
5400
0
    src_stride_argb = dst_stride_argb = 0;
5401
0
  }
5402
0
#if defined(HAS_ARGBCOPYALPHAROW_SSE2)
5403
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5404
0
    ARGBCopyAlphaRow = ARGBCopyAlphaRow_Any_SSE2;
5405
0
    if (IS_ALIGNED(width, 8)) {
5406
0
      ARGBCopyAlphaRow = ARGBCopyAlphaRow_SSE2;
5407
0
    }
5408
0
  }
5409
0
#endif
5410
0
#if defined(HAS_ARGBCOPYALPHAROW_AVX2)
5411
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5412
0
    ARGBCopyAlphaRow = ARGBCopyAlphaRow_Any_AVX2;
5413
0
    if (IS_ALIGNED(width, 16)) {
5414
0
      ARGBCopyAlphaRow = ARGBCopyAlphaRow_AVX2;
5415
0
    }
5416
0
  }
5417
0
#endif
5418
5419
0
  for (y = 0; y < height; ++y) {
5420
0
    ARGBCopyAlphaRow(src_argb, dst_argb, width);
5421
0
    src_argb += src_stride_argb;
5422
0
    dst_argb += dst_stride_argb;
5423
0
  }
5424
0
  return 0;
5425
0
}
5426
5427
// Extract just the alpha channel from ARGB.
5428
LIBYUV_API
5429
int ARGBExtractAlpha(const uint8_t* src_argb,
5430
                     int src_stride_argb,
5431
                     uint8_t* dst_a,
5432
                     int dst_stride_a,
5433
                     int width,
5434
0
                     int height) {
5435
0
  if (!src_argb || !dst_a || width <= 0 || height == 0 || height == INT_MIN) {
5436
0
    return -1;
5437
0
  }
5438
  // Negative height means invert the image.
5439
0
  if (height < 0) {
5440
0
    height = -height;
5441
0
    src_argb += (ptrdiff_t)(height - 1) * src_stride_argb;
5442
0
    src_stride_argb = -src_stride_argb;
5443
0
  }
5444
  // Coalesce rows.
5445
0
  if (src_stride_argb == width * 4 && dst_stride_a == width &&
5446
0
      (ptrdiff_t)width * height <= INT_MAX) {
5447
0
    width *= height;
5448
0
    height = 1;
5449
0
    src_stride_argb = dst_stride_a = 0;
5450
0
  }
5451
0
  void (*ARGBExtractAlphaRow)(const uint8_t* src_argb, uint8_t* dst_a,
5452
0
                              int width) = ARGBExtractAlphaRow_C;
5453
0
#if defined(HAS_ARGBEXTRACTALPHAROW_SSE2)
5454
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5455
0
    ARGBExtractAlphaRow = IS_ALIGNED(width, 8) ? ARGBExtractAlphaRow_SSE2
5456
0
                                               : ARGBExtractAlphaRow_Any_SSE2;
5457
0
  }
5458
0
#endif
5459
0
#if defined(HAS_ARGBEXTRACTALPHAROW_AVX2)
5460
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5461
0
    ARGBExtractAlphaRow = IS_ALIGNED(width, 32) ? ARGBExtractAlphaRow_AVX2
5462
0
                                                : ARGBExtractAlphaRow_Any_AVX2;
5463
0
  }
5464
0
#endif
5465
#if defined(HAS_ARGBEXTRACTALPHAROW_NEON)
5466
  if (TestCpuFlag(kCpuHasNEON)) {
5467
    ARGBExtractAlphaRow = IS_ALIGNED(width, 16) ? ARGBExtractAlphaRow_NEON
5468
                                                : ARGBExtractAlphaRow_Any_NEON;
5469
  }
5470
#endif
5471
#if defined(HAS_ARGBEXTRACTALPHAROW_LSX)
5472
  if (TestCpuFlag(kCpuHasLSX)) {
5473
    ARGBExtractAlphaRow = IS_ALIGNED(width, 16) ? ARGBExtractAlphaRow_LSX
5474
                                                : ARGBExtractAlphaRow_Any_LSX;
5475
  }
5476
#endif
5477
#if defined(HAS_ARGBEXTRACTALPHAROW_RVV)
5478
  if (TestCpuFlag(kCpuHasRVV)) {
5479
    ARGBExtractAlphaRow = ARGBExtractAlphaRow_RVV;
5480
  }
5481
#endif
5482
5483
0
  for (int y = 0; y < height; ++y) {
5484
0
    ARGBExtractAlphaRow(src_argb, dst_a, width);
5485
0
    src_argb += src_stride_argb;
5486
0
    dst_a += dst_stride_a;
5487
0
  }
5488
0
  return 0;
5489
0
}
5490
5491
// Copy a planar Y channel to the alpha channel of a destination ARGB image.
5492
LIBYUV_API
5493
int ARGBCopyYToAlpha(const uint8_t* src_y,
5494
                     int src_stride_y,
5495
                     uint8_t* dst_argb,
5496
                     int dst_stride_argb,
5497
                     int width,
5498
0
                     int height) {
5499
0
  int y;
5500
0
  void (*ARGBCopyYToAlphaRow)(const uint8_t* src_y, uint8_t* dst_argb,
5501
0
                              int width) = ARGBCopyYToAlphaRow_C;
5502
0
  if (!src_y || !dst_argb || width <= 0 || height == 0 || height == INT_MIN) {
5503
0
    return -1;
5504
0
  }
5505
  // Negative height means invert the image.
5506
0
  if (height < 0) {
5507
0
    height = -height;
5508
0
    src_y = src_y + (ptrdiff_t)(height - 1) * src_stride_y;
5509
0
    src_stride_y = -src_stride_y;
5510
0
  }
5511
  // Coalesce rows.
5512
0
  if (src_stride_y == width && dst_stride_argb == width * 4 &&
5513
0
      (ptrdiff_t)width * height <= INT_MAX) {
5514
0
    width *= height;
5515
0
    height = 1;
5516
0
    src_stride_y = dst_stride_argb = 0;
5517
0
  }
5518
0
#if defined(HAS_ARGBCOPYYTOALPHAROW_SSE2)
5519
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5520
0
    ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_Any_SSE2;
5521
0
    if (IS_ALIGNED(width, 8)) {
5522
0
      ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_SSE2;
5523
0
    }
5524
0
  }
5525
0
#endif
5526
0
#if defined(HAS_ARGBCOPYYTOALPHAROW_AVX2)
5527
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5528
0
    ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_Any_AVX2;
5529
0
    if (IS_ALIGNED(width, 16)) {
5530
0
      ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_AVX2;
5531
0
    }
5532
0
  }
5533
0
#endif
5534
#if defined(HAS_ARGBCOPYYTOALPHAROW_RVV)
5535
  if (TestCpuFlag(kCpuHasRVV)) {
5536
    ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_RVV;
5537
  }
5538
#endif
5539
5540
0
  for (y = 0; y < height; ++y) {
5541
0
    ARGBCopyYToAlphaRow(src_y, dst_argb, width);
5542
0
    src_y += src_stride_y;
5543
0
    dst_argb += dst_stride_argb;
5544
0
  }
5545
0
  return 0;
5546
0
}
5547
5548
LIBYUV_API
5549
int YUY2ToNV12(const uint8_t* src_yuy2,
5550
               int src_stride_yuy2,
5551
               uint8_t* dst_y,
5552
               int dst_stride_y,
5553
               uint8_t* dst_uv,
5554
               int dst_stride_uv,
5555
               int width,
5556
0
               int height) {
5557
0
  int y;
5558
0
  void (*YUY2ToYRow)(const uint8_t* src_yuy2, uint8_t* dst_y, int width) =
5559
0
      YUY2ToYRow_C;
5560
0
  void (*YUY2ToNVUVRow)(const uint8_t* src_yuy2, int stride_yuy2,
5561
0
                        uint8_t* dst_uv, int width) = YUY2ToNVUVRow_C;
5562
0
  if (!src_yuy2 || !dst_y || !dst_uv || width <= 0 || height == 0 ||
5563
0
      height == INT_MIN) {
5564
0
    return -1;
5565
0
  }
5566
5567
  // Negative height means invert the image.
5568
0
  if (height < 0) {
5569
0
    height = -height;
5570
0
    src_yuy2 = src_yuy2 + (ptrdiff_t)(height - 1) * src_stride_yuy2;
5571
0
    src_stride_yuy2 = -src_stride_yuy2;
5572
0
  }
5573
0
#if defined(HAS_YUY2TOYROW_SSE2)
5574
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5575
0
    YUY2ToYRow = YUY2ToYRow_Any_SSE2;
5576
0
    if (IS_ALIGNED(width, 16)) {
5577
0
      YUY2ToYRow = YUY2ToYRow_SSE2;
5578
0
    }
5579
0
  }
5580
0
#endif
5581
0
#if defined(HAS_YUY2TOYROW_AVX2)
5582
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5583
0
    YUY2ToYRow = YUY2ToYRow_Any_AVX2;
5584
0
    if (IS_ALIGNED(width, 32)) {
5585
0
      YUY2ToYRow = YUY2ToYRow_AVX2;
5586
0
    }
5587
0
  }
5588
0
#endif
5589
#if defined(HAS_YUY2TOYROW_NEON)
5590
  if (TestCpuFlag(kCpuHasNEON)) {
5591
    YUY2ToYRow = YUY2ToYRow_Any_NEON;
5592
    if (IS_ALIGNED(width, 16)) {
5593
      YUY2ToYRow = YUY2ToYRow_NEON;
5594
    }
5595
  }
5596
#endif
5597
#if defined(HAS_YUY2TOYROW_SVE2)
5598
  if (TestCpuFlag(kCpuHasSVE2)) {
5599
    YUY2ToYRow = YUY2ToYRow_SVE2;
5600
  }
5601
#endif
5602
#if defined(HAS_YUY2TOYROW_LSX) && defined(HAS_YUY2TOUV422ROW_LSX)
5603
  if (TestCpuFlag(kCpuHasLSX)) {
5604
    YUY2ToYRow = YUY2ToYRow_Any_LSX;
5605
    if (IS_ALIGNED(width, 16)) {
5606
      YUY2ToYRow = YUY2ToYRow_LSX;
5607
    }
5608
  }
5609
#endif
5610
#if defined(HAS_YUY2TOYROW_LASX) && defined(HAS_YUY2TOUV422ROW_LASX)
5611
  if (TestCpuFlag(kCpuHasLASX)) {
5612
    YUY2ToYRow = YUY2ToYRow_Any_LASX;
5613
    if (IS_ALIGNED(width, 32)) {
5614
      YUY2ToYRow = YUY2ToYRow_LASX;
5615
    }
5616
  }
5617
#endif
5618
5619
0
#if defined(HAS_YUY2TONVUVROW_SSE2)
5620
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5621
0
    YUY2ToNVUVRow = YUY2ToNVUVRow_Any_SSE2;
5622
0
    if (IS_ALIGNED(width, 16)) {
5623
0
      YUY2ToNVUVRow = YUY2ToNVUVRow_SSE2;
5624
0
    }
5625
0
  }
5626
0
#endif
5627
0
#if defined(HAS_YUY2TONVUVROW_AVX2)
5628
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5629
0
    YUY2ToNVUVRow = YUY2ToNVUVRow_Any_AVX2;
5630
0
    if (IS_ALIGNED(width, 32)) {
5631
0
      YUY2ToNVUVRow = YUY2ToNVUVRow_AVX2;
5632
0
    }
5633
0
  }
5634
0
#endif
5635
#if defined(HAS_YUY2TONVUVROW_NEON)
5636
  if (TestCpuFlag(kCpuHasNEON)) {
5637
    YUY2ToNVUVRow = YUY2ToNVUVRow_Any_NEON;
5638
    if (IS_ALIGNED(width, 16)) {
5639
      YUY2ToNVUVRow = YUY2ToNVUVRow_NEON;
5640
    }
5641
  }
5642
#endif
5643
5644
0
  for (y = 0; y < height - 1; y += 2) {
5645
0
    YUY2ToYRow(src_yuy2, dst_y, width);
5646
0
    YUY2ToYRow(src_yuy2 + src_stride_yuy2, dst_y + dst_stride_y, width);
5647
0
    YUY2ToNVUVRow(src_yuy2, src_stride_yuy2, dst_uv, width);
5648
0
    src_yuy2 += src_stride_yuy2 * 2;
5649
0
    dst_y += dst_stride_y * 2;
5650
0
    dst_uv += dst_stride_uv;
5651
0
  }
5652
0
  if (height & 1) {
5653
0
    YUY2ToYRow(src_yuy2, dst_y, width);
5654
0
    YUY2ToNVUVRow(src_yuy2, 0, dst_uv, width);
5655
0
  }
5656
0
  return 0;
5657
0
}
5658
5659
LIBYUV_API
5660
int UYVYToNV12(const uint8_t* src_uyvy,
5661
               int src_stride_uyvy,
5662
               uint8_t* dst_y,
5663
               int dst_stride_y,
5664
               uint8_t* dst_uv,
5665
               int dst_stride_uv,
5666
               int width,
5667
0
               int height) {
5668
0
  int y;
5669
0
  int halfwidth = (width + 1) >> 1;
5670
0
  void (*SplitUVRow)(const uint8_t* src_uv, uint8_t* dst_u, uint8_t* dst_v,
5671
0
                     int width) = SplitUVRow_C;
5672
0
  void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr,
5673
0
                         ptrdiff_t src_stride, int dst_width,
5674
0
                         int source_y_fraction) = InterpolateRow_C;
5675
5676
0
  if (!src_uyvy || !dst_y || !dst_uv || width <= 0 || height == 0 ||
5677
0
      height == INT_MIN) {
5678
0
    return -1;
5679
0
  }
5680
5681
  // Negative height means invert the image.
5682
0
  if (height < 0) {
5683
0
    height = -height;
5684
0
    src_uyvy = src_uyvy + (ptrdiff_t)(height - 1) * src_stride_uyvy;
5685
0
    src_stride_uyvy = -src_stride_uyvy;
5686
0
  }
5687
0
#if defined(HAS_SPLITUVROW_SSE2)
5688
0
  if (TestCpuFlag(kCpuHasSSE2)) {
5689
0
    SplitUVRow = SplitUVRow_Any_SSE2;
5690
0
    if (IS_ALIGNED(width, 16)) {
5691
0
      SplitUVRow = SplitUVRow_SSE2;
5692
0
    }
5693
0
  }
5694
0
#endif
5695
0
#if defined(HAS_SPLITUVROW_AVX2)
5696
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5697
0
    SplitUVRow = SplitUVRow_Any_AVX2;
5698
0
    if (IS_ALIGNED(width, 32)) {
5699
0
      SplitUVRow = SplitUVRow_AVX2;
5700
0
    }
5701
0
  }
5702
0
#endif
5703
#if defined(HAS_SPLITUVROW_NEON)
5704
  if (TestCpuFlag(kCpuHasNEON)) {
5705
    SplitUVRow = SplitUVRow_Any_NEON;
5706
    if (IS_ALIGNED(width, 16)) {
5707
      SplitUVRow = SplitUVRow_NEON;
5708
    }
5709
  }
5710
#endif
5711
#if defined(HAS_SPLITUVROW_SVE2)
5712
  if (TestCpuFlag(kCpuHasSVE2)) {
5713
    SplitUVRow = SplitUVRow_SVE2;
5714
  }
5715
#endif
5716
#if defined(HAS_SPLITUVROW_LSX)
5717
  if (TestCpuFlag(kCpuHasLSX)) {
5718
    SplitUVRow = SplitUVRow_Any_LSX;
5719
    if (IS_ALIGNED(width, 32)) {
5720
      SplitUVRow = SplitUVRow_LSX;
5721
    }
5722
  }
5723
#endif
5724
#if defined(HAS_SPLITUVROW_RVV)
5725
  if (TestCpuFlag(kCpuHasRVV)) {
5726
    SplitUVRow = SplitUVRow_RVV;
5727
  }
5728
#endif
5729
5730
0
#if defined(HAS_INTERPOLATEROW_AVX2)
5731
0
  if (TestCpuFlag(kCpuHasAVX2)) {
5732
0
    InterpolateRow = InterpolateRow_Any_AVX2;
5733
0
    if (IS_ALIGNED(width, 32)) {
5734
0
      InterpolateRow = InterpolateRow_AVX2;
5735
0
    }
5736
0
  }
5737
0
#endif
5738
#if defined(HAS_INTERPOLATEROW_NEON)
5739
  if (TestCpuFlag(kCpuHasNEON)) {
5740
    InterpolateRow = InterpolateRow_Any_NEON;
5741
    if (IS_ALIGNED(width, 16)) {
5742
      InterpolateRow = InterpolateRow_NEON;
5743
    }
5744
  }
5745
#endif
5746
#if defined(HAS_INTERPOLATEROW_SVE2)
5747
  if (TestCpuFlag(kCpuHasSVE2)) {
5748
    InterpolateRow = InterpolateRow_SVE2;
5749
  }
5750
#endif
5751
#if defined(HAS_INTERPOLATEROW_SME)
5752
  if (TestCpuFlag(kCpuHasSME)) {
5753
    InterpolateRow = InterpolateRow_SME;
5754
  }
5755
#endif
5756
#if defined(HAS_INTERPOLATEROW_LSX)
5757
  if (TestCpuFlag(kCpuHasLSX)) {
5758
    InterpolateRow = InterpolateRow_Any_LSX;
5759
    if (IS_ALIGNED(width, 32)) {
5760
      InterpolateRow = InterpolateRow_LSX;
5761
    }
5762
  }
5763
#endif
5764
#if defined(HAS_INTERPOLATEROW_RVV)
5765
  if (TestCpuFlag(kCpuHasRVV)) {
5766
    InterpolateRow = InterpolateRow_RVV;
5767
  }
5768
#endif
5769
5770
0
  {
5771
0
    int awidth = halfwidth * 2;
5772
    // row of y and 2 rows of uv
5773
0
    align_buffer_64(rows, awidth * 3);
5774
0
    if (!rows)
5775
0
      return 1;
5776
5777
0
    for (y = 0; y < height - 1; y += 2) {
5778
      // Split Y from UV.
5779
0
      SplitUVRow(src_uyvy, rows + awidth, rows, awidth);
5780
0
      memcpy(dst_y, rows, width);
5781
0
      SplitUVRow(src_uyvy + src_stride_uyvy, rows + awidth * 2, rows, awidth);
5782
0
      memcpy(dst_y + dst_stride_y, rows, width);
5783
0
      InterpolateRow(dst_uv, rows + awidth, awidth, awidth, 128);
5784
0
      src_uyvy += src_stride_uyvy * 2;
5785
0
      dst_y += dst_stride_y * 2;
5786
0
      dst_uv += dst_stride_uv;
5787
0
    }
5788
0
    if (height & 1) {
5789
      // Split Y from UV.
5790
0
      SplitUVRow(src_uyvy, dst_uv, rows, awidth);
5791
0
      memcpy(dst_y, rows, width);
5792
0
    }
5793
0
    free_aligned_buffer_64(rows);
5794
0
  }
5795
0
  return 0;
5796
0
}
5797
5798
// width and height are src size allowing odd size handling.
5799
LIBYUV_API
5800
void HalfMergeUVPlane(const uint8_t* src_u,
5801
                      int src_stride_u,
5802
                      const uint8_t* src_v,
5803
                      int src_stride_v,
5804
                      uint8_t* dst_uv,
5805
                      int dst_stride_uv,
5806
                      int width,
5807
0
                      int height) {
5808
0
  int y;
5809
0
  void (*HalfMergeUVRow)(const uint8_t* src_u, int src_stride_u,
5810
0
                         const uint8_t* src_v, int src_stride_v,
5811
0
                         uint8_t* dst_uv, int width) = HalfMergeUVRow_C;
5812
5813
0
  if (width <= 0 || height == 0 || height == INT_MIN) {
5814
0
    return;
5815
0
  }
5816
  // Negative height means invert the image.
5817
0
  if (height < 0) {
5818
0
    height = -height;
5819
0
    src_u = src_u + (ptrdiff_t)(height - 1) * src_stride_u;
5820
0
    src_v = src_v + (ptrdiff_t)(height - 1) * src_stride_v;
5821
0
    src_stride_u = -src_stride_u;
5822
0
    src_stride_v = -src_stride_v;
5823
0
  }
5824
#if defined(HAS_HALFMERGEUVROW_NEON)
5825
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 16)) {
5826
    HalfMergeUVRow = HalfMergeUVRow_NEON;
5827
  }
5828
#endif
5829
#if defined(HAS_HALFMERGEUVROW_SVE2)
5830
  if (TestCpuFlag(kCpuHasSVE2)) {
5831
    HalfMergeUVRow = HalfMergeUVRow_SVE2;
5832
  }
5833
#endif
5834
0
#if defined(HAS_HALFMERGEUVROW_SSSE3)
5835
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
5836
0
    HalfMergeUVRow = HalfMergeUVRow_SSSE3;
5837
0
  }
5838
0
#endif
5839
0
#if defined(HAS_HALFMERGEUVROW_AVX2)
5840
0
  if (TestCpuFlag(kCpuHasAVX2) && IS_ALIGNED(width, 32)) {
5841
0
    HalfMergeUVRow = HalfMergeUVRow_AVX2;
5842
0
  }
5843
0
#endif
5844
5845
0
  for (y = 0; y < height - 1; y += 2) {
5846
    // Merge a row of U and V into a row of UV.
5847
0
    HalfMergeUVRow(src_u, src_stride_u, src_v, src_stride_v, dst_uv, width);
5848
0
    src_u += src_stride_u * 2;
5849
0
    src_v += src_stride_v * 2;
5850
0
    dst_uv += dst_stride_uv;
5851
0
  }
5852
0
  if (height & 1) {
5853
0
    HalfMergeUVRow(src_u, 0, src_v, 0, dst_uv, width);
5854
0
  }
5855
0
}
5856
5857
#ifdef __cplusplus
5858
}  // extern "C"
5859
}  // namespace libyuv
5860
#endif