xref: /aosp_15_r20/external/libvpx/third_party/libyuv/source/rotate.cc (revision fb1b10ab9aebc7c7068eedab379b749d7e3900be)
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 "libyuv/convert.h"
14 #include "libyuv/cpu_id.h"
15 #include "libyuv/planar_functions.h"
16 #include "libyuv/rotate_row.h"
17 #include "libyuv/row.h"
18 
19 #ifdef __cplusplus
20 namespace libyuv {
21 extern "C" {
22 #endif
23 
24 LIBYUV_API
TransposePlane(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)25 void TransposePlane(const uint8_t* src,
26                     int src_stride,
27                     uint8_t* dst,
28                     int dst_stride,
29                     int width,
30                     int height) {
31   int i = height;
32 #if defined(HAS_TRANSPOSEWX16_MSA)
33   void (*TransposeWx16)(const uint8_t* src, int src_stride, uint8_t* dst,
34                         int dst_stride, int width) = TransposeWx16_C;
35 #else
36   void (*TransposeWx8)(const uint8_t* src, int src_stride, uint8_t* dst,
37                        int dst_stride, int width) = TransposeWx8_C;
38 #endif
39 #if defined(HAS_TRANSPOSEWX8_NEON)
40   if (TestCpuFlag(kCpuHasNEON)) {
41     TransposeWx8 = TransposeWx8_NEON;
42   }
43 #endif
44 #if defined(HAS_TRANSPOSEWX8_SSSE3)
45   if (TestCpuFlag(kCpuHasSSSE3)) {
46     TransposeWx8 = TransposeWx8_Any_SSSE3;
47     if (IS_ALIGNED(width, 8)) {
48       TransposeWx8 = TransposeWx8_SSSE3;
49     }
50   }
51 #endif
52 #if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
53   if (TestCpuFlag(kCpuHasSSSE3)) {
54     TransposeWx8 = TransposeWx8_Fast_Any_SSSE3;
55     if (IS_ALIGNED(width, 16)) {
56       TransposeWx8 = TransposeWx8_Fast_SSSE3;
57     }
58   }
59 #endif
60 #if defined(HAS_TRANSPOSEWX16_MSA)
61   if (TestCpuFlag(kCpuHasMSA)) {
62     TransposeWx16 = TransposeWx16_Any_MSA;
63     if (IS_ALIGNED(width, 16)) {
64       TransposeWx16 = TransposeWx16_MSA;
65     }
66   }
67 #endif
68 
69 #if defined(HAS_TRANSPOSEWX16_MSA)
70   // Work across the source in 16x16 tiles
71   while (i >= 16) {
72     TransposeWx16(src, src_stride, dst, dst_stride, width);
73     src += 16 * src_stride;  // Go down 16 rows.
74     dst += 16;               // Move over 16 columns.
75     i -= 16;
76   }
77 #else
78   // Work across the source in 8x8 tiles
79   while (i >= 8) {
80     TransposeWx8(src, src_stride, dst, dst_stride, width);
81     src += 8 * src_stride;  // Go down 8 rows.
82     dst += 8;               // Move over 8 columns.
83     i -= 8;
84   }
85 #endif
86 
87   if (i > 0) {
88     TransposeWxH_C(src, src_stride, dst, dst_stride, width, i);
89   }
90 }
91 
92 LIBYUV_API
RotatePlane90(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)93 void RotatePlane90(const uint8_t* src,
94                    int src_stride,
95                    uint8_t* dst,
96                    int dst_stride,
97                    int width,
98                    int height) {
99   // Rotate by 90 is a transpose with the source read
100   // from bottom to top. So set the source pointer to the end
101   // of the buffer and flip the sign of the source stride.
102   src += src_stride * (height - 1);
103   src_stride = -src_stride;
104   TransposePlane(src, src_stride, dst, dst_stride, width, height);
105 }
106 
107 LIBYUV_API
RotatePlane270(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)108 void RotatePlane270(const uint8_t* src,
109                     int src_stride,
110                     uint8_t* dst,
111                     int dst_stride,
112                     int width,
113                     int height) {
114   // Rotate by 270 is a transpose with the destination written
115   // from bottom to top. So set the destination pointer to the end
116   // of the buffer and flip the sign of the destination stride.
117   dst += dst_stride * (width - 1);
118   dst_stride = -dst_stride;
119   TransposePlane(src, src_stride, dst, dst_stride, width, height);
120 }
121 
122 LIBYUV_API
RotatePlane180(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height)123 void RotatePlane180(const uint8_t* src,
124                     int src_stride,
125                     uint8_t* dst,
126                     int dst_stride,
127                     int width,
128                     int height) {
129   // Swap first and last row and mirror the content. Uses a temporary row.
130   align_buffer_64(row, width);
131   const uint8_t* src_bot = src + src_stride * (height - 1);
132   uint8_t* dst_bot = dst + dst_stride * (height - 1);
133   int half_height = (height + 1) >> 1;
134   int y;
135   void (*MirrorRow)(const uint8_t* src, uint8_t* dst, int width) = MirrorRow_C;
136   void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C;
137 #if defined(HAS_MIRRORROW_NEON)
138   if (TestCpuFlag(kCpuHasNEON)) {
139     MirrorRow = MirrorRow_Any_NEON;
140     if (IS_ALIGNED(width, 16)) {
141       MirrorRow = MirrorRow_NEON;
142     }
143   }
144 #endif
145 #if defined(HAS_MIRRORROW_SSSE3)
146   if (TestCpuFlag(kCpuHasSSSE3)) {
147     MirrorRow = MirrorRow_Any_SSSE3;
148     if (IS_ALIGNED(width, 16)) {
149       MirrorRow = MirrorRow_SSSE3;
150     }
151   }
152 #endif
153 #if defined(HAS_MIRRORROW_AVX2)
154   if (TestCpuFlag(kCpuHasAVX2)) {
155     MirrorRow = MirrorRow_Any_AVX2;
156     if (IS_ALIGNED(width, 32)) {
157       MirrorRow = MirrorRow_AVX2;
158     }
159   }
160 #endif
161 #if defined(HAS_MIRRORROW_MSA)
162   if (TestCpuFlag(kCpuHasMSA)) {
163     MirrorRow = MirrorRow_Any_MSA;
164     if (IS_ALIGNED(width, 64)) {
165       MirrorRow = MirrorRow_MSA;
166     }
167   }
168 #endif
169 #if defined(HAS_COPYROW_SSE2)
170   if (TestCpuFlag(kCpuHasSSE2)) {
171     CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
172   }
173 #endif
174 #if defined(HAS_COPYROW_AVX)
175   if (TestCpuFlag(kCpuHasAVX)) {
176     CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
177   }
178 #endif
179 #if defined(HAS_COPYROW_ERMS)
180   if (TestCpuFlag(kCpuHasERMS)) {
181     CopyRow = CopyRow_ERMS;
182   }
183 #endif
184 #if defined(HAS_COPYROW_NEON)
185   if (TestCpuFlag(kCpuHasNEON)) {
186     CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
187   }
188 #endif
189 
190   // Odd height will harmlessly mirror the middle row twice.
191   for (y = 0; y < half_height; ++y) {
192     MirrorRow(src, row, width);  // Mirror first row into a buffer
193     src += src_stride;
194     MirrorRow(src_bot, dst, width);  // Mirror last row into first row
195     dst += dst_stride;
196     CopyRow(row, dst_bot, width);  // Copy first mirrored row into last
197     src_bot -= src_stride;
198     dst_bot -= dst_stride;
199   }
200   free_aligned_buffer_64(row);
201 }
202 
203 LIBYUV_API
TransposeUV(const uint8_t * src,int src_stride,uint8_t * dst_a,int dst_stride_a,uint8_t * dst_b,int dst_stride_b,int width,int height)204 void TransposeUV(const uint8_t* src,
205                  int src_stride,
206                  uint8_t* dst_a,
207                  int dst_stride_a,
208                  uint8_t* dst_b,
209                  int dst_stride_b,
210                  int width,
211                  int height) {
212   int i = height;
213 #if defined(HAS_TRANSPOSEUVWX16_MSA)
214   void (*TransposeUVWx16)(const uint8_t* src, int src_stride, uint8_t* dst_a,
215                           int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
216                           int width) = TransposeUVWx16_C;
217 #else
218   void (*TransposeUVWx8)(const uint8_t* src, int src_stride, uint8_t* dst_a,
219                          int dst_stride_a, uint8_t* dst_b, int dst_stride_b,
220                          int width) = TransposeUVWx8_C;
221 #endif
222 #if defined(HAS_TRANSPOSEUVWX8_NEON)
223   if (TestCpuFlag(kCpuHasNEON)) {
224     TransposeUVWx8 = TransposeUVWx8_NEON;
225   }
226 #endif
227 #if defined(HAS_TRANSPOSEUVWX8_SSE2)
228   if (TestCpuFlag(kCpuHasSSE2)) {
229     TransposeUVWx8 = TransposeUVWx8_Any_SSE2;
230     if (IS_ALIGNED(width, 8)) {
231       TransposeUVWx8 = TransposeUVWx8_SSE2;
232     }
233   }
234 #endif
235 #if defined(HAS_TRANSPOSEUVWX16_MSA)
236   if (TestCpuFlag(kCpuHasMSA)) {
237     TransposeUVWx16 = TransposeUVWx16_Any_MSA;
238     if (IS_ALIGNED(width, 8)) {
239       TransposeUVWx16 = TransposeUVWx16_MSA;
240     }
241   }
242 #endif
243 
244 #if defined(HAS_TRANSPOSEUVWX16_MSA)
245   // Work through the source in 8x8 tiles.
246   while (i >= 16) {
247     TransposeUVWx16(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
248                     width);
249     src += 16 * src_stride;  // Go down 16 rows.
250     dst_a += 16;             // Move over 8 columns.
251     dst_b += 16;             // Move over 8 columns.
252     i -= 16;
253   }
254 #else
255   // Work through the source in 8x8 tiles.
256   while (i >= 8) {
257     TransposeUVWx8(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
258                    width);
259     src += 8 * src_stride;  // Go down 8 rows.
260     dst_a += 8;             // Move over 8 columns.
261     dst_b += 8;             // Move over 8 columns.
262     i -= 8;
263   }
264 #endif
265 
266   if (i > 0) {
267     TransposeUVWxH_C(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b,
268                      width, i);
269   }
270 }
271 
272 LIBYUV_API
RotateUV90(const uint8_t * src,int src_stride,uint8_t * dst_a,int dst_stride_a,uint8_t * dst_b,int dst_stride_b,int width,int height)273 void RotateUV90(const uint8_t* src,
274                 int src_stride,
275                 uint8_t* dst_a,
276                 int dst_stride_a,
277                 uint8_t* dst_b,
278                 int dst_stride_b,
279                 int width,
280                 int height) {
281   src += src_stride * (height - 1);
282   src_stride = -src_stride;
283 
284   TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width,
285               height);
286 }
287 
288 LIBYUV_API
RotateUV270(const uint8_t * src,int src_stride,uint8_t * dst_a,int dst_stride_a,uint8_t * dst_b,int dst_stride_b,int width,int height)289 void RotateUV270(const uint8_t* src,
290                  int src_stride,
291                  uint8_t* dst_a,
292                  int dst_stride_a,
293                  uint8_t* dst_b,
294                  int dst_stride_b,
295                  int width,
296                  int height) {
297   dst_a += dst_stride_a * (width - 1);
298   dst_b += dst_stride_b * (width - 1);
299   dst_stride_a = -dst_stride_a;
300   dst_stride_b = -dst_stride_b;
301 
302   TransposeUV(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, width,
303               height);
304 }
305 
306 // Rotate 180 is a horizontal and vertical flip.
307 LIBYUV_API
RotateUV180(const uint8_t * src,int src_stride,uint8_t * dst_a,int dst_stride_a,uint8_t * dst_b,int dst_stride_b,int width,int height)308 void RotateUV180(const uint8_t* src,
309                  int src_stride,
310                  uint8_t* dst_a,
311                  int dst_stride_a,
312                  uint8_t* dst_b,
313                  int dst_stride_b,
314                  int width,
315                  int height) {
316   int i;
317   void (*MirrorUVRow)(const uint8_t* src, uint8_t* dst_u, uint8_t* dst_v,
318                       int width) = MirrorUVRow_C;
319 #if defined(HAS_MIRRORUVROW_NEON)
320   if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
321     MirrorUVRow = MirrorUVRow_NEON;
322   }
323 #endif
324 #if defined(HAS_MIRRORUVROW_SSSE3)
325   if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 16)) {
326     MirrorUVRow = MirrorUVRow_SSSE3;
327   }
328 #endif
329 #if defined(HAS_MIRRORUVROW_MSA)
330   if (TestCpuFlag(kCpuHasMSA) && IS_ALIGNED(width, 32)) {
331     MirrorUVRow = MirrorUVRow_MSA;
332   }
333 #endif
334 
335   dst_a += dst_stride_a * (height - 1);
336   dst_b += dst_stride_b * (height - 1);
337 
338   for (i = 0; i < height; ++i) {
339     MirrorUVRow(src, dst_a, dst_b, width);
340     src += src_stride;
341     dst_a -= dst_stride_a;
342     dst_b -= dst_stride_b;
343   }
344 }
345 
346 LIBYUV_API
RotatePlane(const uint8_t * src,int src_stride,uint8_t * dst,int dst_stride,int width,int height,enum RotationMode mode)347 int RotatePlane(const uint8_t* src,
348                 int src_stride,
349                 uint8_t* dst,
350                 int dst_stride,
351                 int width,
352                 int height,
353                 enum RotationMode mode) {
354   if (!src || width <= 0 || height == 0 || !dst) {
355     return -1;
356   }
357 
358   // Negative height means invert the image.
359   if (height < 0) {
360     height = -height;
361     src = src + (height - 1) * src_stride;
362     src_stride = -src_stride;
363   }
364 
365   switch (mode) {
366     case kRotate0:
367       // copy frame
368       CopyPlane(src, src_stride, dst, dst_stride, width, height);
369       return 0;
370     case kRotate90:
371       RotatePlane90(src, src_stride, dst, dst_stride, width, height);
372       return 0;
373     case kRotate270:
374       RotatePlane270(src, src_stride, dst, dst_stride, width, height);
375       return 0;
376     case kRotate180:
377       RotatePlane180(src, src_stride, dst, dst_stride, width, height);
378       return 0;
379     default:
380       break;
381   }
382   return -1;
383 }
384 
385 LIBYUV_API
I420Rotate(const uint8_t * src_y,int src_stride_y,const uint8_t * src_u,int src_stride_u,const uint8_t * src_v,int src_stride_v,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)386 int I420Rotate(const uint8_t* src_y,
387                int src_stride_y,
388                const uint8_t* src_u,
389                int src_stride_u,
390                const uint8_t* src_v,
391                int src_stride_v,
392                uint8_t* dst_y,
393                int dst_stride_y,
394                uint8_t* dst_u,
395                int dst_stride_u,
396                uint8_t* dst_v,
397                int dst_stride_v,
398                int width,
399                int height,
400                enum RotationMode mode) {
401   int halfwidth = (width + 1) >> 1;
402   int halfheight = (height + 1) >> 1;
403   if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
404       !dst_u || !dst_v) {
405     return -1;
406   }
407 
408   // Negative height means invert the image.
409   if (height < 0) {
410     height = -height;
411     halfheight = (height + 1) >> 1;
412     src_y = src_y + (height - 1) * src_stride_y;
413     src_u = src_u + (halfheight - 1) * src_stride_u;
414     src_v = src_v + (halfheight - 1) * src_stride_v;
415     src_stride_y = -src_stride_y;
416     src_stride_u = -src_stride_u;
417     src_stride_v = -src_stride_v;
418   }
419 
420   switch (mode) {
421     case kRotate0:
422       // copy frame
423       return I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v,
424                       src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
425                       dst_v, dst_stride_v, width, height);
426     case kRotate90:
427       RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
428       RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
429                     halfheight);
430       RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
431                     halfheight);
432       return 0;
433     case kRotate270:
434       RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
435       RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
436                      halfheight);
437       RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
438                      halfheight);
439       return 0;
440     case kRotate180:
441       RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
442       RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
443                      halfheight);
444       RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
445                      halfheight);
446       return 0;
447     default:
448       break;
449   }
450   return -1;
451 }
452 
453 LIBYUV_API
NV12ToI420Rotate(const uint8_t * src_y,int src_stride_y,const uint8_t * src_uv,int src_stride_uv,uint8_t * dst_y,int dst_stride_y,uint8_t * dst_u,int dst_stride_u,uint8_t * dst_v,int dst_stride_v,int width,int height,enum RotationMode mode)454 int NV12ToI420Rotate(const uint8_t* src_y,
455                      int src_stride_y,
456                      const uint8_t* src_uv,
457                      int src_stride_uv,
458                      uint8_t* dst_y,
459                      int dst_stride_y,
460                      uint8_t* dst_u,
461                      int dst_stride_u,
462                      uint8_t* dst_v,
463                      int dst_stride_v,
464                      int width,
465                      int height,
466                      enum RotationMode mode) {
467   int halfwidth = (width + 1) >> 1;
468   int halfheight = (height + 1) >> 1;
469   if (!src_y || !src_uv || width <= 0 || height == 0 || !dst_y || !dst_u ||
470       !dst_v) {
471     return -1;
472   }
473 
474   // Negative height means invert the image.
475   if (height < 0) {
476     height = -height;
477     halfheight = (height + 1) >> 1;
478     src_y = src_y + (height - 1) * src_stride_y;
479     src_uv = src_uv + (halfheight - 1) * src_stride_uv;
480     src_stride_y = -src_stride_y;
481     src_stride_uv = -src_stride_uv;
482   }
483 
484   switch (mode) {
485     case kRotate0:
486       // copy frame
487       return NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, dst_y,
488                         dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
489                         width, height);
490     case kRotate90:
491       RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
492       RotateUV90(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
493                  dst_stride_v, halfwidth, halfheight);
494       return 0;
495     case kRotate270:
496       RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
497       RotateUV270(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
498                   dst_stride_v, halfwidth, halfheight);
499       return 0;
500     case kRotate180:
501       RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
502       RotateUV180(src_uv, src_stride_uv, dst_u, dst_stride_u, dst_v,
503                   dst_stride_v, halfwidth, halfheight);
504       return 0;
505     default:
506       break;
507   }
508   return -1;
509 }
510 
511 #ifdef __cplusplus
512 }  // extern "C"
513 }  // namespace libyuv
514 #endif
515