rotate.cc 18 KB

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