Coverage Report

Created: 2025-07-12 06:45

/src/libavif/ext/libyuv/source/rotate.cc
Line
Count
Source (jump to first uncovered line)
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 <assert.h>
12
13
#include "libyuv/rotate.h"
14
15
#include "libyuv/convert.h"
16
#include "libyuv/cpu_id.h"
17
#include "libyuv/planar_functions.h"
18
#include "libyuv/rotate_row.h"
19
#include "libyuv/row.h"
20
21
#ifdef __cplusplus
22
namespace libyuv {
23
extern "C" {
24
#endif
25
26
LIBYUV_API
27
void TransposePlane(const uint8_t* src,
28
                    int src_stride,
29
                    uint8_t* dst,
30
                    int dst_stride,
31
                    int width,
32
0
                    int height) {
33
0
  int i = height;
34
#if defined(HAS_TRANSPOSEWXH_SME)
35
  void (*TransposeWxH)(const uint8_t* src, int src_stride, uint8_t* dst,
36
                       int dst_stride, int width, int height) = NULL;
37
#endif
38
#if defined(HAS_TRANSPOSEWX16_MSA) || defined(HAS_TRANSPOSEWX16_LSX) || \
39
    defined(HAS_TRANSPOSEWX16_NEON)
40
  void (*TransposeWx16)(const uint8_t* src, int src_stride, uint8_t* dst,
41
                        int dst_stride, int width) = TransposeWx16_C;
42
#else
43
0
  void (*TransposeWx8)(const uint8_t* src, int src_stride, uint8_t* dst,
44
0
                       int dst_stride, int width) = TransposeWx8_C;
45
0
#endif
46
47
#if defined(HAS_TRANSPOSEWX8_NEON)
48
  if (TestCpuFlag(kCpuHasNEON)) {
49
    TransposeWx8 = TransposeWx8_Any_NEON;
50
    if (IS_ALIGNED(width, 8)) {
51
      TransposeWx8 = TransposeWx8_NEON;
52
    }
53
  }
54
#endif
55
#if defined(HAS_TRANSPOSEWX16_NEON)
56
  if (TestCpuFlag(kCpuHasNEON)) {
57
    TransposeWx16 = TransposeWx16_Any_NEON;
58
    if (IS_ALIGNED(width, 16)) {
59
      TransposeWx16 = TransposeWx16_NEON;
60
    }
61
  }
62
#endif
63
#if defined(HAS_TRANSPOSEWXH_SME)
64
  if (TestCpuFlag(kCpuHasSME)) {
65
    TransposeWxH = TransposeWxH_SME;
66
  }
67
#endif
68
0
#if defined(HAS_TRANSPOSEWX8_SSSE3)
69
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
70
0
    TransposeWx8 = TransposeWx8_Any_SSSE3;
71
0
    if (IS_ALIGNED(width, 8)) {
72
0
      TransposeWx8 = TransposeWx8_SSSE3;
73
0
    }
74
0
  }
75
0
#endif
76
0
#if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
77
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
78
0
    TransposeWx8 = TransposeWx8_Fast_Any_SSSE3;
79
0
    if (IS_ALIGNED(width, 16)) {
80
0
      TransposeWx8 = TransposeWx8_Fast_SSSE3;
81
0
    }
82
0
  }
83
0
#endif
84
#if defined(HAS_TRANSPOSEWX16_MSA)
85
  if (TestCpuFlag(kCpuHasMSA)) {
86
    TransposeWx16 = TransposeWx16_Any_MSA;
87
    if (IS_ALIGNED(width, 16)) {
88
      TransposeWx16 = TransposeWx16_MSA;
89
    }
90
  }
91
#endif
92
#if defined(HAS_TRANSPOSEWX16_LSX)
93
  if (TestCpuFlag(kCpuHasLSX)) {
94
    TransposeWx16 = TransposeWx16_Any_LSX;
95
    if (IS_ALIGNED(width, 16)) {
96
      TransposeWx16 = TransposeWx16_LSX;
97
    }
98
  }
99
#endif
100
101
#if defined(HAS_TRANSPOSEWXH_SME)
102
  if (TransposeWxH) {
103
    TransposeWxH(src, src_stride, dst, dst_stride, width, height);
104
    return;
105
  }
106
#endif
107
#if defined(HAS_TRANSPOSEWX16_MSA) || defined(HAS_TRANSPOSEWX16_LSX) || \
108
    defined(HAS_TRANSPOSEWX16_NEON)
109
  // Work across the source in 16x16 tiles
110
  while (i >= 16) {
111
    TransposeWx16(src, src_stride, dst, dst_stride, width);
112
    src += 16 * src_stride;  // Go down 16 rows.
113
    dst += 16;               // Move over 16 columns.
114
    i -= 16;
115
  }
116
#else
117
  // Work across the source in 8x8 tiles
118
0
  while (i >= 8) {
119
0
    TransposeWx8(src, src_stride, dst, dst_stride, width);
120
0
    src += 8 * src_stride;  // Go down 8 rows.
121
0
    dst += 8;               // Move over 8 columns.
122
0
    i -= 8;
123
0
  }
124
0
#endif
125
126
0
  if (i > 0) {
127
0
    TransposeWxH_C(src, src_stride, dst, dst_stride, width, i);
128
0
  }
129
0
}
130
131
LIBYUV_API
132
void RotatePlane90(const uint8_t* src,
133
                   int src_stride,
134
                   uint8_t* dst,
135
                   int dst_stride,
136
                   int width,
137
0
                   int height) {
138
  // Rotate by 90 is a transpose with the source read
139
  // from bottom to top. So set the source pointer to the end
140
  // of the buffer and flip the sign of the source stride.
141
0
  src += src_stride * (height - 1);
142
0
  src_stride = -src_stride;
143
0
  TransposePlane(src, src_stride, dst, dst_stride, width, height);
144
0
}
145
146
LIBYUV_API
147
void RotatePlane270(const uint8_t* src,
148
                    int src_stride,
149
                    uint8_t* dst,
150
                    int dst_stride,
151
                    int width,
152
0
                    int height) {
153
  // Rotate by 270 is a transpose with the destination written
154
  // from bottom to top. So set the destination pointer to the end
155
  // of the buffer and flip the sign of the destination stride.
156
0
  dst += dst_stride * (width - 1);
157
0
  dst_stride = -dst_stride;
158
0
  TransposePlane(src, src_stride, dst, dst_stride, width, height);
159
0
}
160
161
LIBYUV_API
162
void RotatePlane180(const uint8_t* src,
163
                    int src_stride,
164
                    uint8_t* dst,
165
                    int dst_stride,
166
                    int width,
167
0
                    int height) {
168
  // Swap top and bottom row and mirror the content. Uses a temporary row.
169
0
  align_buffer_64(row, width);
170
0
  assert(row);
171
0
  if (!row)
172
0
    return;
173
0
  const uint8_t* src_bot = src + src_stride * (height - 1);
174
0
  uint8_t* dst_bot = dst + dst_stride * (height - 1);
175
0
  int half_height = (height + 1) >> 1;
176
0
  int y;
177
0
  void (*MirrorRow)(const uint8_t* src, uint8_t* dst, int width) = MirrorRow_C;
178
0
  void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
179
#if defined(HAS_MIRRORROW_NEON)
180
  if (TestCpuFlag(kCpuHasNEON)) {
181
    MirrorRow = MirrorRow_Any_NEON;
182
    if (IS_ALIGNED(width, 32)) {
183
      MirrorRow = MirrorRow_NEON;
184
    }
185
  }
186
#endif
187
0
#if defined(HAS_MIRRORROW_SSSE3)
188
0
  if (TestCpuFlag(kCpuHasSSSE3)) {
189
0
    MirrorRow = MirrorRow_Any_SSSE3;
190
0
    if (IS_ALIGNED(width, 16)) {
191
0
      MirrorRow = MirrorRow_SSSE3;
192
0
    }
193
0
  }
194
0
#endif
195
0
#if defined(HAS_MIRRORROW_AVX2)
196
0
  if (TestCpuFlag(kCpuHasAVX2)) {
197
0
    MirrorRow = MirrorRow_Any_AVX2;
198
0
    if (IS_ALIGNED(width, 32)) {
199
0
      MirrorRow = MirrorRow_AVX2;
200
0
    }
201
0
  }
202
0
#endif
203
#if defined(HAS_MIRRORROW_MSA)
204
  if (TestCpuFlag(kCpuHasMSA)) {
205
    MirrorRow = MirrorRow_Any_MSA;
206
    if (IS_ALIGNED(width, 64)) {
207
      MirrorRow = MirrorRow_MSA;
208
    }
209
  }
210
#endif
211
#if defined(HAS_MIRRORROW_LSX)
212
  if (TestCpuFlag(kCpuHasLSX)) {
213
    MirrorRow = MirrorRow_Any_LSX;
214
    if (IS_ALIGNED(width, 32)) {
215
      MirrorRow = MirrorRow_LSX;
216
    }
217
  }
218
#endif
219
#if defined(HAS_MIRRORROW_LASX)
220
  if (TestCpuFlag(kCpuHasLASX)) {
221
    MirrorRow = MirrorRow_Any_LASX;
222
    if (IS_ALIGNED(width, 64)) {
223
      MirrorRow = MirrorRow_LASX;
224
    }
225
  }
226
#endif
227
0
#if defined(HAS_COPYROW_SSE2)
228
0
  if (TestCpuFlag(kCpuHasSSE2)) {
229
0
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
230
0
  }
231
0
#endif
232
0
#if defined(HAS_COPYROW_AVX)
233
0
  if (TestCpuFlag(kCpuHasAVX)) {
234
0
    CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
235
0
  }
236
0
#endif
237
0
#if defined(HAS_COPYROW_AVX512BW)
238
0
  if (TestCpuFlag(kCpuHasAVX512BW)) {
239
0
    CopyRow = IS_ALIGNED(width, 128) ? CopyRow_AVX512BW : CopyRow_Any_AVX512BW;
240
0
  }
241
0
#endif
242
0
#if defined(HAS_COPYROW_ERMS)
243
0
  if (TestCpuFlag(kCpuHasERMS)) {
244
0
    CopyRow = CopyRow_ERMS;
245
0
  }
246
0
#endif
247
#if defined(HAS_COPYROW_NEON)
248
  if (TestCpuFlag(kCpuHasNEON)) {
249
    CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
250
  }
251
#endif
252
#if defined(HAS_COPYROW_SME)
253
  if (TestCpuFlag(kCpuHasSME)) {
254
    CopyRow = CopyRow_SME;
255
  }
256
#endif
257
#if defined(HAS_COPYROW_RVV)
258
  if (TestCpuFlag(kCpuHasRVV)) {
259
    CopyRow = CopyRow_RVV;
260
  }
261
#endif
262
263
  // Odd height will harmlessly mirror the middle row twice.
264
0
  for (y = 0; y < half_height; ++y) {
265
0
    CopyRow(src, row, width);        // Copy top row into buffer
266
0
    MirrorRow(src_bot, dst, width);  // Mirror bottom row into top row
267
0
    MirrorRow(row, dst_bot, width);  // Mirror buffer into bottom row
268
0
    src += src_stride;
269
0
    dst += dst_stride;
270
0
    src_bot -= src_stride;
271
0
    dst_bot -= dst_stride;
272
0
  }
273
0
  free_aligned_buffer_64(row);
274
0
}
275
276
LIBYUV_API
277
void SplitTransposeUV(const uint8_t* src,
278
                      int src_stride,
279
                      uint8_t* dst_a,
280
                      int dst_stride_a,
281
                      uint8_t* dst_b,
282
                      int dst_stride_b,
283
                      int width,
284
0
                      int height) {
285
0
  int i = height;
286
#if defined(HAS_TRANSPOSEUVWXH_SME)
287
  void (*TransposeUVWxH)(const uint8_t* src, int src_stride, uint8_t* dst_a,
288
                         int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
289
                         int width, int height) = TransposeUVWxH_C;
290
#endif
291
#if defined(HAS_TRANSPOSEUVWX16_MSA)
292
  void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a,
293
                          int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
294
                          int width) = TransposeUVWx16_C;
295
#elif defined(HAS_TRANSPOSEUVWX16_LSX)
296
  void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a,
297
                          int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
298
                          int width) = TransposeUVWx16_C;
299
#else
300
0
  void (*TransposeUVWx8)(const uint8_t* src, int src_stride, uint8_t* dst_a,
301
0
                         int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
302
0
                         int width) = TransposeUVWx8_C;
303
0
#endif
304
305
#if defined(HAS_TRANSPOSEUVWX16_MSA)
306
  if (TestCpuFlag(kCpuHasMSA)) {
307
    TransposeUVWx16 = TransposeUVWx16_Any_MSA;
308
    if (IS_ALIGNED(width, 8)) {
309
      TransposeUVWx16 = TransposeUVWx16_MSA;
310
    }
311
  }
312
#elif defined(HAS_TRANSPOSEUVWX16_LSX)
313
  if (TestCpuFlag(kCpuHasLSX)) {
314
    TransposeUVWx16 = TransposeUVWx16_Any_LSX;
315
    if (IS_ALIGNED(width, 8)) {
316
      TransposeUVWx16 = TransposeUVWx16_LSX;
317
    }
318
  }
319
#else
320
#if defined(HAS_TRANSPOSEUVWX8_NEON)
321
  if (TestCpuFlag(kCpuHasNEON)) {
322
    TransposeUVWx8 = TransposeUVWx8_Any_NEON;
323
    if (IS_ALIGNED(width, 8)) {
324
      TransposeUVWx8 = TransposeUVWx8_NEON;
325
    }
326
  }
327
#endif
328
#if defined(HAS_TRANSPOSEUVWXH_SME)
329
  if (TestCpuFlag(kCpuHasSME)) {
330
    TransposeUVWxH = TransposeUVWxH_SME;
331
  }
332
#endif
333
0
#if defined(HAS_TRANSPOSEUVWX8_SSE2)
334
0
  if (TestCpuFlag(kCpuHasSSE2)) {
335
0
    TransposeUVWx8 = TransposeUVWx8_Any_SSE2;
336
0
    if (IS_ALIGNED(width, 8)) {
337
0
      TransposeUVWx8 = TransposeUVWx8_SSE2;
338
0
    }
339
0
  }
340
0
#endif
341
0
#endif /* defined(HAS_TRANSPOSEUVWX16_MSA) */
342
343
#if defined(HAS_TRANSPOSEUVWXH_SME)
344
  if (TestCpuFlag(kCpuHasSME)) {
345
    TransposeUVWxH(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
346
                   width, i);
347
    return;
348
  }
349
#endif
350
#if defined(HAS_TRANSPOSEUVWX16_MSA)
351
  // Work through the source in 8x8 tiles.
352
  while (i >= 16) {
353
    TransposeUVWx16(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
354
                    width);
355
    src += 16 * src_stride;  // Go down 16 rows.
356
    dst_a += 16;             // Move over 8 columns.
357
    dst_b += 16;             // Move over 8 columns.
358
    i -= 16;
359
  }
360
#elif defined(HAS_TRANSPOSEUVWX16_LSX)
361
  // Work through the source in 8x8 tiles.
362
  while (i >= 16) {
363
    TransposeUVWx16(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
364
                    width);
365
    src += 16 * src_stride;  // Go down 16 rows.
366
    dst_a += 16;             // Move over 8 columns.
367
    dst_b += 16;             // Move over 8 columns.
368
    i -= 16;
369
  }
370
#else
371
  // Work through the source in 8x8 tiles.
372
0
  while (i >= 8) {
373
0
    TransposeUVWx8(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
374
0
                   width);
375
0
    src += 8 * src_stride;  // Go down 8 rows.
376
0
    dst_a += 8;             // Move over 8 columns.
377
0
    dst_b += 8;             // Move over 8 columns.
378
0
    i -= 8;
379
0
  }
380
0
#endif
381
382
0
  if (i > 0) {
383
0
    TransposeUVWxH_C(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
384
0
                     width, i);
385
0
  }
386
0
}
387
388
LIBYUV_API
389
void SplitRotateUV90(const uint8_t* src,
390
                     int src_stride,
391
                     uint8_t* dst_a,
392
                     int dst_stride_a,
393
                     uint8_t* dst_b,
394
                     int dst_stride_b,
395
                     int width,
396
0
                     int height) {
397
0
  src += src_stride * (height - 1);
398
0
  src_stride = -src_stride;
399
400
0
  SplitTransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
401
0
                   width, height);
402
0
}
403
404
LIBYUV_API
405
void SplitRotateUV270(const uint8_t* src,
406
                      int src_stride,
407
                      uint8_t* dst_a,
408
                      int dst_stride_a,
409
                      uint8_t* dst_b,
410
                      int dst_stride_b,
411
                      int width,
412
0
                      int height) {
413
0
  dst_a += dst_stride_a * (width - 1);
414
0
  dst_b += dst_stride_b * (width - 1);
415
0
  dst_stride_a = -dst_stride_a;
416
0
  dst_stride_b = -dst_stride_b;
417
418
0
  SplitTransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
419
0
                   width, height);
420
0
}
421
422
// Rotate 180 is a horizontal and vertical flip.
423
LIBYUV_API
424
void SplitRotateUV180(const uint8_t* src,
425
                      int src_stride,
426
                      uint8_t* dst_a,
427
                      int dst_stride_a,
428
                      uint8_t* dst_b,
429
                      int dst_stride_b,
430
                      int width,
431
0
                      int height) {
432
0
  int i;
433
0
  void (*MirrorSplitUVRow)(const uint8_t* src, uint8_t* dst_u, uint8_t* dst_v,
434
0
                           int width) = MirrorSplitUVRow_C;
435
#if defined(HAS_MIRRORSPLITUVROW_NEON)
436
  if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 16)) {
437
    MirrorSplitUVRow = MirrorSplitUVRow_NEON;
438
  }
439
#endif
440
0
#if defined(HAS_MIRRORSPLITUVROW_SSSE3)
441
0
  if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
442
0
    MirrorSplitUVRow = MirrorSplitUVRow_SSSE3;
443
0
  }
444
0
#endif
445
#if defined(HAS_MIRRORSPLITUVROW_MSA)
446
  if (TestCpuFlag(kCpuHasMSA) && IS_ALIGNED(width, 32)) {
447
    MirrorSplitUVRow = MirrorSplitUVRow_MSA;
448
  }
449
#endif
450
#if defined(HAS_MIRRORSPLITUVROW_LSX)
451
  if (TestCpuFlag(kCpuHasLSX) && IS_ALIGNED(width, 32)) {
452
    MirrorSplitUVRow = MirrorSplitUVRow_LSX;
453
  }
454
#endif
455
456
0
  dst_a += dst_stride_a * (height - 1);
457
0
  dst_b += dst_stride_b * (height - 1);
458
459
0
  for (i = 0; i < height; ++i) {
460
0
    MirrorSplitUVRow(src, dst_a, dst_b, width);
461
0
    src += src_stride;
462
0
    dst_a -= dst_stride_a;
463
0
    dst_b -= dst_stride_b;
464
0
  }
465
0
}
466
467
// Rotate UV and split into planar.
468
// width and height expected to be half size for NV12
469
LIBYUV_API
470
int SplitRotateUV(const uint8_t* src_uv,
471
                  int src_stride_uv,
472
                  uint8_t* dst_u,
473
                  int dst_stride_u,
474
                  uint8_t* dst_v,
475
                  int dst_stride_v,
476
                  int width,
477
                  int height,
478
0
                  enum RotationMode mode) {
479
0
  if (!src_uv || width <= 0 || height == 0 || !dst_u || !dst_v) {
480
0
    return -1;
481
0
  }
482
483
  // Negative height means invert the image.
484
0
  if (height < 0) {
485
0
    height = -height;
486
0
    src_uv = src_uv + (height - 1) * src_stride_uv;
487
0
    src_stride_uv = -src_stride_uv;
488
0
  }
489
490
0
  switch (mode) {
491
0
    case kRotate0:
492
0
      SplitUVPlane(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
493
0
                   dst_stride_v, width, height);
494
0
      return 0;
495
0
    case kRotate90:
496
0
      SplitRotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
497
0
                      dst_stride_v, width, height);
498
0
      return 0;
499
0
    case kRotate270:
500
0
      SplitRotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
501
0
                       dst_stride_v, width, height);
502
0
      return 0;
503
0
    case kRotate180:
504
0
      SplitRotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
505
0
                       dst_stride_v, width, height);
506
0
      return 0;
507
0
    default:
508
0
      break;
509
0
  }
510
0
  return -1;
511
0
}
512
513
LIBYUV_API
514
int RotatePlane(const uint8_t* src,
515
                int src_stride,
516
                uint8_t* dst,
517
                int dst_stride,
518
                int width,
519
                int height,
520
0
                enum RotationMode mode) {
521
0
  if (!src || width <= 0 || height == 0 || !dst) {
522
0
    return -1;
523
0
  }
524
525
  // Negative height means invert the image.
526
0
  if (height < 0) {
527
0
    height = -height;
528
0
    src = src + (height - 1) * src_stride;
529
0
    src_stride = -src_stride;
530
0
  }
531
532
0
  switch (mode) {
533
0
    case kRotate0:
534
      // copy frame
535
0
      CopyPlane(src, src_stride, dst, dst_stride, width, height);
536
0
      return 0;
537
0
    case kRotate90:
538
0
      RotatePlane90(src, src_stride, dst, dst_stride, width, height);
539
0
      return 0;
540
0
    case kRotate270:
541
0
      RotatePlane270(src, src_stride, dst, dst_stride, width, height);
542
0
      return 0;
543
0
    case kRotate180:
544
0
      RotatePlane180(src, src_stride, dst, dst_stride, width, height);
545
0
      return 0;
546
0
    default:
547
0
      break;
548
0
  }
549
0
  return -1;
550
0
}
551
552
static void TransposePlane_16(const uint16_t* src,
553
                              int src_stride,
554
                              uint16_t* dst,
555
                              int dst_stride,
556
                              int width,
557
0
                              int height) {
558
0
  int i = height;
559
  // Work across the source in 8x8 tiles
560
0
  while (i >= 8) {
561
0
    TransposeWx8_16_C(src, src_stride, dst, dst_stride, width);
562
0
    src += 8 * src_stride;  // Go down 8 rows.
563
0
    dst += 8;               // Move over 8 columns.
564
0
    i -= 8;
565
0
  }
566
567
0
  if (i > 0) {
568
0
    TransposeWxH_16_C(src, src_stride, dst, dst_stride, width, i);
569
0
  }
570
0
}
571
572
static void RotatePlane90_16(const uint16_t* src,
573
                             int src_stride,
574
                             uint16_t* dst,
575
                             int dst_stride,
576
                             int width,
577
0
                             int height) {
578
  // Rotate by 90 is a transpose with the source read
579
  // from bottom to top. So set the source pointer to the end
580
  // of the buffer and flip the sign of the source stride.
581
0
  src += src_stride * (height - 1);
582
0
  src_stride = -src_stride;
583
0
  TransposePlane_16(src, src_stride, dst, dst_stride, width, height);
584
0
}
585
586
static void RotatePlane270_16(const uint16_t* src,
587
                              int src_stride,
588
                              uint16_t* dst,
589
                              int dst_stride,
590
                              int width,
591
0
                              int height) {
592
  // Rotate by 270 is a transpose with the destination written
593
  // from bottom to top. So set the destination pointer to the end
594
  // of the buffer and flip the sign of the destination stride.
595
0
  dst += dst_stride * (width - 1);
596
0
  dst_stride = -dst_stride;
597
0
  TransposePlane_16(src, src_stride, dst, dst_stride, width, height);
598
0
}
599
600
static void RotatePlane180_16(const uint16_t* src,
601
                              int src_stride,
602
                              uint16_t* dst,
603
                              int dst_stride,
604
                              int width,
605
0
                              int height) {
606
0
  const uint16_t* src_bot = src + src_stride * (height - 1);
607
0
  uint16_t* dst_bot = dst + dst_stride * (height - 1);
608
0
  int half_height = (height + 1) >> 1;
609
0
  int y;
610
611
  // Swap top and bottom row and mirror the content. Uses a temporary row.
612
0
  align_buffer_64(row, width * 2);
613
0
  uint16_t* row_tmp = (uint16_t*)row;
614
0
  assert(row);
615
0
  if (!row)
616
0
    return;
617
618
  // Odd height will harmlessly mirror the middle row twice.
619
0
  for (y = 0; y < half_height; ++y) {
620
0
    CopyRow_16_C(src, row_tmp, width);        // Copy top row into buffer
621
0
    MirrorRow_16_C(src_bot, dst, width);      // Mirror bottom row into top row
622
0
    MirrorRow_16_C(row_tmp, dst_bot, width);  // Mirror buffer into bottom row
623
0
    src += src_stride;
624
0
    dst += dst_stride;
625
0
    src_bot -= src_stride;
626
0
    dst_bot -= dst_stride;
627
0
  }
628
0
  free_aligned_buffer_64(row);
629
0
}
630
631
LIBYUV_API
632
int RotatePlane_16(const uint16_t* src,
633
                   int src_stride,
634
                   uint16_t* dst,
635
                   int dst_stride,
636
                   int width,
637
                   int height,
638
0
                   enum RotationMode mode) {
639
0
  if (!src || width <= 0 || height == 0 || !dst) {
640
0
    return -1;
641
0
  }
642
643
  // Negative height means invert the image.
644
0
  if (height < 0) {
645
0
    height = -height;
646
0
    src = src + (height - 1) * src_stride;
647
0
    src_stride = -src_stride;
648
0
  }
649
650
0
  switch (mode) {
651
0
    case kRotate0:
652
      // copy frame
653
0
      CopyPlane_16(src, src_stride, dst, dst_stride, width, height);
654
0
      return 0;
655
0
    case kRotate90:
656
0
      RotatePlane90_16(src, src_stride, dst, dst_stride, width, height);
657
0
      return 0;
658
0
    case kRotate270:
659
0
      RotatePlane270_16(src, src_stride, dst, dst_stride, width, height);
660
0
      return 0;
661
0
    case kRotate180:
662
0
      RotatePlane180_16(src, src_stride, dst, dst_stride, width, height);
663
0
      return 0;
664
0
    default:
665
0
      break;
666
0
  }
667
0
  return -1;
668
0
}
669
670
LIBYUV_API
671
int I420Rotate(const uint8_t* src_y,
672
               int src_stride_y,
673
               const uint8_t* src_u,
674
               int src_stride_u,
675
               const uint8_t* src_v,
676
               int src_stride_v,
677
               uint8_t* dst_y,
678
               int dst_stride_y,
679
               uint8_t* dst_u,
680
               int dst_stride_u,
681
               uint8_t* dst_v,
682
               int dst_stride_v,
683
               int width,
684
               int height,
685
0
               enum RotationMode mode) {
686
0
  int halfwidth = (width + 1) >> 1;
687
0
  int halfheight = (height + 1) >> 1;
688
0
  if ((!src_y && dst_y) || !src_u || !src_v || width <= 0 || height == 0 ||
689
0
      !dst_y || !dst_u || !dst_v) {
690
0
    return -1;
691
0
  }
692
693
  // Negative height means invert the image.
694
0
  if (height < 0) {
695
0
    height = -height;
696
0
    halfheight = (height + 1) >> 1;
697
0
    src_y = src_y + (height - 1) * src_stride_y;
698
0
    src_u = src_u + (halfheight - 1) * src_stride_u;
699
0
    src_v = src_v + (halfheight - 1) * src_stride_v;
700
0
    src_stride_y = -src_stride_y;
701
0
    src_stride_u = -src_stride_u;
702
0
    src_stride_v = -src_stride_v;
703
0
  }
704
705
0
  switch (mode) {
706
0
    case kRotate0:
707
      // copy frame
708
0
      return I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v,
709
0
                      src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
710
0
                      dst_v, dst_stride_v, width, height);
711
0
    case kRotate90:
712
0
      RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
713
0
      RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
714
0
                    halfheight);
715
0
      RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
716
0
                    halfheight);
717
0
      return 0;
718
0
    case kRotate270:
719
0
      RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
720
0
      RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
721
0
                     halfheight);
722
0
      RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
723
0
                     halfheight);
724
0
      return 0;
725
0
    case kRotate180:
726
0
      RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
727
0
      RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
728
0
                     halfheight);
729
0
      RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
730
0
                     halfheight);
731
0
      return 0;
732
0
    default:
733
0
      break;
734
0
  }
735
0
  return -1;
736
0
}
737
738
// I422 has half width x full height UV planes, so rotate by 90 and 270
739
// require scaling to maintain 422 subsampling.
740
LIBYUV_API
741
int I422Rotate(const uint8_t* src_y,
742
               int src_stride_y,
743
               const uint8_t* src_u,
744
               int src_stride_u,
745
               const uint8_t* src_v,
746
               int src_stride_v,
747
               uint8_t* dst_y,
748
               int dst_stride_y,
749
               uint8_t* dst_u,
750
               int dst_stride_u,
751
               uint8_t* dst_v,
752
               int dst_stride_v,
753
               int width,
754
               int height,
755
0
               enum RotationMode mode) {
756
0
  int halfwidth = (width + 1) >> 1;
757
0
  int halfheight = (height + 1) >> 1;
758
0
  int r;
759
0
  if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
760
0
      !dst_u || !dst_v) {
761
0
    return -1;
762
0
  }
763
  // Negative height means invert the image.
764
0
  if (height < 0) {
765
0
    height = -height;
766
0
    src_y = src_y + (height - 1) * src_stride_y;
767
0
    src_u = src_u + (height - 1) * src_stride_u;
768
0
    src_v = src_v + (height - 1) * src_stride_v;
769
0
    src_stride_y = -src_stride_y;
770
0
    src_stride_u = -src_stride_u;
771
0
    src_stride_v = -src_stride_v;
772
0
  }
773
774
0
  switch (mode) {
775
0
    case kRotate0:
776
      // Copy frame
777
0
      CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
778
0
      CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
779
0
      CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
780
0
      return 0;
781
782
      // Note on temporary Y plane for UV.
783
      // Rotation of UV first fits within the Y destination plane rows.
784
      // Y plane is width x height
785
      // Y plane rotated is height x width
786
      // UV plane is (width / 2) x height
787
      // UV plane rotated is height x (width / 2)
788
      // UV plane rotated+scaled is (height / 2) x width.
789
      // UV plane rotated is a temporary that fits within the Y plane rotated.
790
791
0
    case kRotate90:
792
0
      RotatePlane90(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
793
0
                    height);
794
0
      r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_u,
795
0
                     dst_stride_u, halfheight, width, kFilterBilinear);
796
0
      if (r != 0) {
797
0
        return r;
798
0
      }
799
0
      RotatePlane90(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
800
0
                    height);
801
0
      r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_v,
802
0
                     dst_stride_v, halfheight, width, kFilterLinear);
803
0
      if (r != 0) {
804
0
        return r;
805
0
      }
806
0
      RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
807
0
      return 0;
808
0
    case kRotate270:
809
0
      RotatePlane270(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
810
0
                     height);
811
0
      r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_u,
812
0
                     dst_stride_u, halfheight, width, kFilterBilinear);
813
0
      if (r != 0) {
814
0
        return r;
815
0
      }
816
0
      RotatePlane270(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
817
0
                     height);
818
0
      r = ScalePlane(dst_y, dst_stride_y, height, halfwidth, dst_v,
819
0
                     dst_stride_v, halfheight, width, kFilterLinear);
820
0
      if (r != 0) {
821
0
        return r;
822
0
      }
823
0
      RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
824
0
      return 0;
825
0
    case kRotate180:
826
0
      RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
827
0
      RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
828
0
                     height);
829
0
      RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
830
0
                     height);
831
0
      return 0;
832
0
    default:
833
0
      break;
834
0
  }
835
0
  return -1;
836
0
}
837
838
LIBYUV_API
839
int I444Rotate(const uint8_t* src_y,
840
               int src_stride_y,
841
               const uint8_t* src_u,
842
               int src_stride_u,
843
               const uint8_t* src_v,
844
               int src_stride_v,
845
               uint8_t* dst_y,
846
               int dst_stride_y,
847
               uint8_t* dst_u,
848
               int dst_stride_u,
849
               uint8_t* dst_v,
850
               int dst_stride_v,
851
               int width,
852
               int height,
853
0
               enum RotationMode mode) {
854
0
  if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
855
0
      !dst_u || !dst_v) {
856
0
    return -1;
857
0
  }
858
859
  // Negative height means invert the image.
860
0
  if (height < 0) {
861
0
    height = -height;
862
0
    src_y = src_y + (height - 1) * src_stride_y;
863
0
    src_u = src_u + (height - 1) * src_stride_u;
864
0
    src_v = src_v + (height - 1) * src_stride_v;
865
0
    src_stride_y = -src_stride_y;
866
0
    src_stride_u = -src_stride_u;
867
0
    src_stride_v = -src_stride_v;
868
0
  }
869
870
0
  switch (mode) {
871
0
    case kRotate0:
872
      // copy frame
873
0
      CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
874
0
      CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
875
0
      CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
876
0
      return 0;
877
0
    case kRotate90:
878
0
      RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
879
0
      RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
880
0
      RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
881
0
      return 0;
882
0
    case kRotate270:
883
0
      RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
884
0
      RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
885
0
      RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
886
0
      return 0;
887
0
    case kRotate180:
888
0
      RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
889
0
      RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
890
0
      RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
891
0
      return 0;
892
0
    default:
893
0
      break;
894
0
  }
895
0
  return -1;
896
0
}
897
898
LIBYUV_API
899
int NV12ToI420Rotate(const uint8_t* src_y,
900
                     int src_stride_y,
901
                     const uint8_t* src_uv,
902
                     int src_stride_uv,
903
                     uint8_t* dst_y,
904
                     int dst_stride_y,
905
                     uint8_t* dst_u,
906
                     int dst_stride_u,
907
                     uint8_t* dst_v,
908
                     int dst_stride_v,
909
                     int width,
910
                     int height,
911
0
                     enum RotationMode mode) {
912
0
  int halfwidth = (width + 1) >> 1;
913
0
  int halfheight = (height + 1) >> 1;
914
0
  if (!src_y || !src_uv || width <= 0 || height == 0 || !dst_y || !dst_u ||
915
0
      !dst_v) {
916
0
    return -1;
917
0
  }
918
919
  // Negative height means invert the image.
920
0
  if (height < 0) {
921
0
    height = -height;
922
0
    halfheight = (height + 1) >> 1;
923
0
    src_y = src_y + (height - 1) * src_stride_y;
924
0
    src_uv = src_uv + (halfheight - 1) * src_stride_uv;
925
0
    src_stride_y = -src_stride_y;
926
0
    src_stride_uv = -src_stride_uv;
927
0
  }
928
929
0
  switch (mode) {
930
0
    case kRotate0:
931
      // copy frame
932
0
      return NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y,
933
0
                        dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
934
0
                        width, height);
935
0
    case kRotate90:
936
0
      RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
937
0
      SplitRotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
938
0
                      dst_stride_v, halfwidth, halfheight);
939
0
      return 0;
940
0
    case kRotate270:
941
0
      RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
942
0
      SplitRotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
943
0
                       dst_stride_v, halfwidth, halfheight);
944
0
      return 0;
945
0
    case kRotate180:
946
0
      RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
947
0
      SplitRotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
948
0
                       dst_stride_v, halfwidth, halfheight);
949
0
      return 0;
950
0
    default:
951
0
      break;
952
0
  }
953
0
  return -1;
954
0
}
955
956
static void SplitPixels(const uint8_t* src_u,
957
                        int src_pixel_stride_uv,
958
                        uint8_t* dst_u,
959
0
                        int width) {
960
0
  int i;
961
0
  for (i = 0; i < width; ++i) {
962
0
    *dst_u = *src_u;
963
0
    ++dst_u;
964
0
    src_u += src_pixel_stride_uv;
965
0
  }
966
0
}
967
968
// Convert Android420 to I420 with Rotate
969
LIBYUV_API
970
int Android420ToI420Rotate(const uint8_t* src_y,
971
                           int src_stride_y,
972
                           const uint8_t* src_u,
973
                           int src_stride_u,
974
                           const uint8_t* src_v,
975
                           int src_stride_v,
976
                           int src_pixel_stride_uv,
977
                           uint8_t* dst_y,
978
                           int dst_stride_y,
979
                           uint8_t* dst_u,
980
                           int dst_stride_u,
981
                           uint8_t* dst_v,
982
                           int dst_stride_v,
983
                           int width,
984
                           int height,
985
0
                           enum RotationMode rotation) {
986
0
  int y;
987
0
  const ptrdiff_t vu_off = src_v - src_u;
988
0
  int halfwidth = (width + 1) >> 1;
989
0
  int halfheight = (height + 1) >> 1;
990
0
  if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
991
0
      height == 0) {
992
0
    return -1;
993
0
  }
994
  // Negative height means invert the image.
995
0
  if (height < 0) {
996
0
    height = -height;
997
0
    halfheight = (height + 1) >> 1;
998
0
    src_y = src_y + (height - 1) * src_stride_y;
999
0
    src_u = src_u + (halfheight - 1) * src_stride_u;
1000
0
    src_v = src_v + (halfheight - 1) * src_stride_v;
1001
0
    src_stride_y = -src_stride_y;
1002
0
    src_stride_u = -src_stride_u;
1003
0
    src_stride_v = -src_stride_v;
1004
0
  }
1005
1006
0
  if (dst_y) {
1007
0
    RotatePlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height,
1008
0
                rotation);
1009
0
  }
1010
1011
  // Copy UV planes - I420
1012
0
  if (src_pixel_stride_uv == 1) {
1013
0
    RotatePlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight,
1014
0
                rotation);
1015
0
    RotatePlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight,
1016
0
                rotation);
1017
0
    return 0;
1018
0
  }
1019
  // Split UV planes - NV21
1020
0
  if (src_pixel_stride_uv == 2 && vu_off == -1 &&
1021
0
      src_stride_u == src_stride_v) {
1022
0
    SplitRotateUV(src_v, src_stride_v, dst_v, dst_stride_v, dst_u, dst_stride_u,
1023
0
                  halfwidth, halfheight, rotation);
1024
0
    return 0;
1025
0
  }
1026
  // Split UV planes - NV12
1027
0
  if (src_pixel_stride_uv == 2 && vu_off == 1 && src_stride_u == src_stride_v) {
1028
0
    SplitRotateUV(src_u, src_stride_u, dst_u, dst_stride_u, dst_v, dst_stride_v,
1029
0
                  halfwidth, halfheight, rotation);
1030
0
    return 0;
1031
0
  }
1032
1033
0
  if (rotation == 0) {
1034
0
    for (y = 0; y < halfheight; ++y) {
1035
0
      SplitPixels(src_u, src_pixel_stride_uv, dst_u, halfwidth);
1036
0
      SplitPixels(src_v, src_pixel_stride_uv, dst_v, halfwidth);
1037
0
      src_u += src_stride_u;
1038
0
      src_v += src_stride_v;
1039
0
      dst_u += dst_stride_u;
1040
0
      dst_v += dst_stride_v;
1041
0
    }
1042
0
    return 0;
1043
0
  }
1044
  // unsupported type and/or rotation.
1045
0
  return -1;
1046
0
}
1047
1048
LIBYUV_API
1049
int I010Rotate(const uint16_t* src_y,
1050
               int src_stride_y,
1051
               const uint16_t* src_u,
1052
               int src_stride_u,
1053
               const uint16_t* src_v,
1054
               int src_stride_v,
1055
               uint16_t* dst_y,
1056
               int dst_stride_y,
1057
               uint16_t* dst_u,
1058
               int dst_stride_u,
1059
               uint16_t* dst_v,
1060
               int dst_stride_v,
1061
               int width,
1062
               int height,
1063
0
               enum RotationMode mode) {
1064
0
  int halfwidth = (width + 1) >> 1;
1065
0
  int halfheight = (height + 1) >> 1;
1066
0
  if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
1067
0
      !dst_u || !dst_v || dst_stride_y < 0) {
1068
0
    return -1;
1069
0
  }
1070
  // Negative height means invert the image.
1071
0
  if (height < 0) {
1072
0
    height = -height;
1073
0
    src_y = src_y + (height - 1) * src_stride_y;
1074
0
    src_u = src_u + (height - 1) * src_stride_u;
1075
0
    src_v = src_v + (height - 1) * src_stride_v;
1076
0
    src_stride_y = -src_stride_y;
1077
0
    src_stride_u = -src_stride_u;
1078
0
    src_stride_v = -src_stride_v;
1079
0
  }
1080
1081
0
  switch (mode) {
1082
0
    case kRotate0:
1083
      // copy frame
1084
0
      return I010Copy(src_y, src_stride_y, src_u, src_stride_u, src_v,
1085
0
                      src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
1086
0
                      dst_v, dst_stride_v, width, height);
1087
0
    case kRotate90:
1088
0
      RotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1089
0
      RotatePlane90_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1090
0
                       halfheight);
1091
0
      RotatePlane90_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1092
0
                       halfheight);
1093
0
      return 0;
1094
0
    case kRotate270:
1095
0
      RotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1096
0
                        height);
1097
0
      RotatePlane270_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1098
0
                        halfheight);
1099
0
      RotatePlane270_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1100
0
                        halfheight);
1101
0
      return 0;
1102
0
    case kRotate180:
1103
0
      RotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1104
0
                        height);
1105
0
      RotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1106
0
                        halfheight);
1107
0
      RotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1108
0
                        halfheight);
1109
0
      return 0;
1110
0
    default:
1111
0
      break;
1112
0
  }
1113
0
  return -1;
1114
0
}
1115
1116
// I210 has half width x full height UV planes, so rotate by 90 and 270
1117
// require scaling to maintain 422 subsampling.
1118
LIBYUV_API
1119
int I210Rotate(const uint16_t* src_y,
1120
               int src_stride_y,
1121
               const uint16_t* src_u,
1122
               int src_stride_u,
1123
               const uint16_t* src_v,
1124
               int src_stride_v,
1125
               uint16_t* dst_y,
1126
               int dst_stride_y,
1127
               uint16_t* dst_u,
1128
               int dst_stride_u,
1129
               uint16_t* dst_v,
1130
               int dst_stride_v,
1131
               int width,
1132
               int height,
1133
0
               enum RotationMode mode) {
1134
0
  int halfwidth = (width + 1) >> 1;
1135
0
  int halfheight = (height + 1) >> 1;
1136
0
  int r;
1137
0
  if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
1138
0
      !dst_u || !dst_v) {
1139
0
    return -1;
1140
0
  }
1141
  // Negative height means invert the image.
1142
0
  if (height < 0) {
1143
0
    height = -height;
1144
0
    src_y = src_y + (height - 1) * src_stride_y;
1145
0
    src_u = src_u + (height - 1) * src_stride_u;
1146
0
    src_v = src_v + (height - 1) * src_stride_v;
1147
0
    src_stride_y = -src_stride_y;
1148
0
    src_stride_u = -src_stride_u;
1149
0
    src_stride_v = -src_stride_v;
1150
0
  }
1151
1152
0
  switch (mode) {
1153
0
    case kRotate0:
1154
      // Copy frame
1155
0
      CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1156
0
      CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
1157
0
      CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
1158
0
      return 0;
1159
1160
      // Note on temporary Y plane for UV.
1161
      // Rotation of UV first fits within the Y destination plane rows.
1162
      // Y plane is width x height
1163
      // Y plane rotated is height x width
1164
      // UV plane is (width / 2) x height
1165
      // UV plane rotated is height x (width / 2)
1166
      // UV plane rotated+scaled is (height / 2) x width.
1167
      // UV plane rotated is a temporary that fits within the Y plane rotated.
1168
1169
0
    case kRotate90:
1170
0
      RotatePlane90_16(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
1171
0
                       height);
1172
0
      r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_u,
1173
0
                        dst_stride_u, halfheight, width, kFilterBilinear);
1174
0
      if (r != 0) {
1175
0
        return r;
1176
0
      }
1177
0
      RotatePlane90_16(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
1178
0
                       height);
1179
0
      r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_v,
1180
0
                        dst_stride_v, halfheight, width, kFilterLinear);
1181
0
      if (r != 0) {
1182
0
        return r;
1183
0
      }
1184
0
      RotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1185
0
      return 0;
1186
0
    case kRotate270:
1187
0
      RotatePlane270_16(src_u, src_stride_u, dst_y, dst_stride_y, halfwidth,
1188
0
                        height);
1189
0
      r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_u,
1190
0
                        dst_stride_u, halfheight, width, kFilterBilinear);
1191
0
      if (r != 0) {
1192
0
        return r;
1193
0
      }
1194
0
      RotatePlane270_16(src_v, src_stride_v, dst_y, dst_stride_y, halfwidth,
1195
0
                        height);
1196
0
      r = ScalePlane_16(dst_y, dst_stride_y, height, halfwidth, dst_v,
1197
0
                        dst_stride_v, halfheight, width, kFilterLinear);
1198
0
      if (r != 0) {
1199
0
        return r;
1200
0
      }
1201
0
      RotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1202
0
                        height);
1203
0
      return 0;
1204
0
    case kRotate180:
1205
0
      RotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1206
0
                        height);
1207
0
      RotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
1208
0
                        height);
1209
0
      RotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
1210
0
                        height);
1211
0
      return 0;
1212
0
    default:
1213
0
      break;
1214
0
  }
1215
0
  return -1;
1216
0
}
1217
1218
LIBYUV_API
1219
int I410Rotate(const uint16_t* src_y,
1220
               int src_stride_y,
1221
               const uint16_t* src_u,
1222
               int src_stride_u,
1223
               const uint16_t* src_v,
1224
               int src_stride_v,
1225
               uint16_t* dst_y,
1226
               int dst_stride_y,
1227
               uint16_t* dst_u,
1228
               int dst_stride_u,
1229
               uint16_t* dst_v,
1230
               int dst_stride_v,
1231
               int width,
1232
               int height,
1233
0
               enum RotationMode mode) {
1234
0
  if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
1235
0
      !dst_u || !dst_v || dst_stride_y < 0) {
1236
0
    return -1;
1237
0
  }
1238
  // Negative height means invert the image.
1239
0
  if (height < 0) {
1240
0
    height = -height;
1241
0
    src_y = src_y + (height - 1) * src_stride_y;
1242
0
    src_u = src_u + (height - 1) * src_stride_u;
1243
0
    src_v = src_v + (height - 1) * src_stride_v;
1244
0
    src_stride_y = -src_stride_y;
1245
0
    src_stride_u = -src_stride_u;
1246
0
    src_stride_v = -src_stride_v;
1247
0
  }
1248
1249
0
  switch (mode) {
1250
0
    case kRotate0:
1251
      // copy frame
1252
0
      CopyPlane_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1253
0
      CopyPlane_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
1254
0
      CopyPlane_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
1255
0
      return 0;
1256
0
    case kRotate90:
1257
0
      RotatePlane90_16(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
1258
0
      RotatePlane90_16(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
1259
0
      RotatePlane90_16(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
1260
0
      return 0;
1261
0
    case kRotate270:
1262
0
      RotatePlane270_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1263
0
                        height);
1264
0
      RotatePlane270_16(src_u, src_stride_u, dst_u, dst_stride_u, width,
1265
0
                        height);
1266
0
      RotatePlane270_16(src_v, src_stride_v, dst_v, dst_stride_v, width,
1267
0
                        height);
1268
0
      return 0;
1269
0
    case kRotate180:
1270
0
      RotatePlane180_16(src_y, src_stride_y, dst_y, dst_stride_y, width,
1271
0
                        height);
1272
0
      RotatePlane180_16(src_u, src_stride_u, dst_u, dst_stride_u, width,
1273
0
                        height);
1274
0
      RotatePlane180_16(src_v, src_stride_v, dst_v, dst_stride_v, width,
1275
0
                        height);
1276
0
      return 0;
1277
0
    default:
1278
0
      break;
1279
0
  }
1280
0
  return -1;
1281
0
}
1282
1283
#ifdef __cplusplus
1284
}  // extern "C"
1285
}  // namespace libyuv
1286
#endif