rotate.cc 18 KB

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