Coverage Report

Created: 2026-08-31 07:01

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