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