planar_functions.cc 83 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811
  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/planar_functions.h"
  11. #include <string.h> // for memset()
  12. #include "libyuv/cpu_id.h"
  13. #ifdef HAVE_JPEG
  14. #include "libyuv/mjpeg_decoder.h"
  15. #endif
  16. #include "libyuv/row.h"
  17. #include "libyuv/scale_row.h" // for ScaleRowDown2
  18. #ifdef __cplusplus
  19. namespace libyuv {
  20. extern "C" {
  21. #endif
  22. // Copy a plane of data
  23. LIBYUV_API
  24. void CopyPlane(const uint8* src_y, int src_stride_y,
  25. uint8* dst_y, int dst_stride_y,
  26. int width, int height) {
  27. int y;
  28. void (*CopyRow)(const uint8* src, uint8* dst, int width) = CopyRow_C;
  29. // Negative height means invert the image.
  30. if (height < 0) {
  31. height = -height;
  32. dst_y = dst_y + (height - 1) * dst_stride_y;
  33. dst_stride_y = -dst_stride_y;
  34. }
  35. // Coalesce rows.
  36. if (src_stride_y == width &&
  37. dst_stride_y == width) {
  38. width *= height;
  39. height = 1;
  40. src_stride_y = dst_stride_y = 0;
  41. }
  42. // Nothing to do.
  43. if (src_y == dst_y && src_stride_y == dst_stride_y) {
  44. return;
  45. }
  46. #if defined(HAS_COPYROW_SSE2)
  47. if (TestCpuFlag(kCpuHasSSE2)) {
  48. CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
  49. }
  50. #endif
  51. #if defined(HAS_COPYROW_AVX)
  52. if (TestCpuFlag(kCpuHasAVX)) {
  53. CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
  54. }
  55. #endif
  56. #if defined(HAS_COPYROW_ERMS)
  57. if (TestCpuFlag(kCpuHasERMS)) {
  58. CopyRow = CopyRow_ERMS;
  59. }
  60. #endif
  61. #if defined(HAS_COPYROW_NEON)
  62. if (TestCpuFlag(kCpuHasNEON)) {
  63. CopyRow = IS_ALIGNED(width, 32) ? CopyRow_NEON : CopyRow_Any_NEON;
  64. }
  65. #endif
  66. #if defined(HAS_COPYROW_MIPS)
  67. if (TestCpuFlag(kCpuHasMIPS)) {
  68. CopyRow = CopyRow_MIPS;
  69. }
  70. #endif
  71. // Copy plane
  72. for (y = 0; y < height; ++y) {
  73. CopyRow(src_y, dst_y, width);
  74. src_y += src_stride_y;
  75. dst_y += dst_stride_y;
  76. }
  77. }
  78. // TODO(fbarchard): Consider support for negative height.
  79. LIBYUV_API
  80. void CopyPlane_16(const uint16* src_y, int src_stride_y,
  81. uint16* dst_y, int dst_stride_y,
  82. int width, int height) {
  83. int y;
  84. void (*CopyRow)(const uint16* src, uint16* dst, int width) = CopyRow_16_C;
  85. // Coalesce rows.
  86. if (src_stride_y == width &&
  87. dst_stride_y == width) {
  88. width *= height;
  89. height = 1;
  90. src_stride_y = dst_stride_y = 0;
  91. }
  92. #if defined(HAS_COPYROW_16_SSE2)
  93. if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 32)) {
  94. CopyRow = CopyRow_16_SSE2;
  95. }
  96. #endif
  97. #if defined(HAS_COPYROW_16_ERMS)
  98. if (TestCpuFlag(kCpuHasERMS)) {
  99. CopyRow = CopyRow_16_ERMS;
  100. }
  101. #endif
  102. #if defined(HAS_COPYROW_16_NEON)
  103. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 32)) {
  104. CopyRow = CopyRow_16_NEON;
  105. }
  106. #endif
  107. #if defined(HAS_COPYROW_16_MIPS)
  108. if (TestCpuFlag(kCpuHasMIPS)) {
  109. CopyRow = CopyRow_16_MIPS;
  110. }
  111. #endif
  112. // Copy plane
  113. for (y = 0; y < height; ++y) {
  114. CopyRow(src_y, dst_y, width);
  115. src_y += src_stride_y;
  116. dst_y += dst_stride_y;
  117. }
  118. }
  119. // Copy I422.
  120. LIBYUV_API
  121. int I422Copy(const uint8* src_y, int src_stride_y,
  122. const uint8* src_u, int src_stride_u,
  123. const uint8* src_v, int src_stride_v,
  124. uint8* dst_y, int dst_stride_y,
  125. uint8* dst_u, int dst_stride_u,
  126. uint8* dst_v, int dst_stride_v,
  127. int width, int height) {
  128. int halfwidth = (width + 1) >> 1;
  129. if (!src_u || !src_v ||
  130. !dst_u || !dst_v ||
  131. width <= 0 || height == 0) {
  132. return -1;
  133. }
  134. // Negative height means invert the image.
  135. if (height < 0) {
  136. height = -height;
  137. src_y = src_y + (height - 1) * src_stride_y;
  138. src_u = src_u + (height - 1) * src_stride_u;
  139. src_v = src_v + (height - 1) * src_stride_v;
  140. src_stride_y = -src_stride_y;
  141. src_stride_u = -src_stride_u;
  142. src_stride_v = -src_stride_v;
  143. }
  144. if (dst_y) {
  145. CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  146. }
  147. CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, height);
  148. CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, height);
  149. return 0;
  150. }
  151. // Copy I444.
  152. LIBYUV_API
  153. int I444Copy(const uint8* src_y, int src_stride_y,
  154. const uint8* src_u, int src_stride_u,
  155. const uint8* src_v, int src_stride_v,
  156. uint8* dst_y, int dst_stride_y,
  157. uint8* dst_u, int dst_stride_u,
  158. uint8* dst_v, int dst_stride_v,
  159. int width, int height) {
  160. if (!src_u || !src_v ||
  161. !dst_u || !dst_v ||
  162. width <= 0 || height == 0) {
  163. return -1;
  164. }
  165. // Negative height means invert the image.
  166. if (height < 0) {
  167. height = -height;
  168. src_y = src_y + (height - 1) * src_stride_y;
  169. src_u = src_u + (height - 1) * src_stride_u;
  170. src_v = src_v + (height - 1) * src_stride_v;
  171. src_stride_y = -src_stride_y;
  172. src_stride_u = -src_stride_u;
  173. src_stride_v = -src_stride_v;
  174. }
  175. if (dst_y) {
  176. CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  177. }
  178. CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height);
  179. CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height);
  180. return 0;
  181. }
  182. // Copy I400.
  183. LIBYUV_API
  184. int I400ToI400(const uint8* src_y, int src_stride_y,
  185. uint8* dst_y, int dst_stride_y,
  186. int width, int height) {
  187. if (!src_y || !dst_y || width <= 0 || height == 0) {
  188. return -1;
  189. }
  190. // Negative height means invert the image.
  191. if (height < 0) {
  192. height = -height;
  193. src_y = src_y + (height - 1) * src_stride_y;
  194. src_stride_y = -src_stride_y;
  195. }
  196. CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  197. return 0;
  198. }
  199. // Convert I420 to I400.
  200. LIBYUV_API
  201. int I420ToI400(const uint8* src_y, int src_stride_y,
  202. const uint8* src_u, int src_stride_u,
  203. const uint8* src_v, int src_stride_v,
  204. uint8* dst_y, int dst_stride_y,
  205. int width, int height) {
  206. if (!src_y || !dst_y || width <= 0 || height == 0) {
  207. return -1;
  208. }
  209. // Negative height means invert the image.
  210. if (height < 0) {
  211. height = -height;
  212. src_y = src_y + (height - 1) * src_stride_y;
  213. src_stride_y = -src_stride_y;
  214. }
  215. CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  216. return 0;
  217. }
  218. // Support function for NV12 etc UV channels.
  219. // Width and height are plane sizes (typically half pixel width).
  220. LIBYUV_API
  221. void SplitUVPlane(const uint8* src_uv, int src_stride_uv,
  222. uint8* dst_u, int dst_stride_u,
  223. uint8* dst_v, int dst_stride_v,
  224. int width, int height) {
  225. int y;
  226. void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
  227. int width) = SplitUVRow_C;
  228. // Negative height means invert the image.
  229. if (height < 0) {
  230. height = -height;
  231. dst_u = dst_u + (height - 1) * dst_stride_u;
  232. dst_v = dst_v + (height - 1) * dst_stride_v;
  233. dst_stride_u = -dst_stride_u;
  234. dst_stride_v = -dst_stride_v;
  235. }
  236. // Coalesce rows.
  237. if (src_stride_uv == width * 2 &&
  238. dst_stride_u == width &&
  239. dst_stride_v == width) {
  240. width *= height;
  241. height = 1;
  242. src_stride_uv = dst_stride_u = dst_stride_v = 0;
  243. }
  244. #if defined(HAS_SPLITUVROW_SSE2)
  245. if (TestCpuFlag(kCpuHasSSE2)) {
  246. SplitUVRow = SplitUVRow_Any_SSE2;
  247. if (IS_ALIGNED(width, 16)) {
  248. SplitUVRow = SplitUVRow_SSE2;
  249. }
  250. }
  251. #endif
  252. #if defined(HAS_SPLITUVROW_AVX2)
  253. if (TestCpuFlag(kCpuHasAVX2)) {
  254. SplitUVRow = SplitUVRow_Any_AVX2;
  255. if (IS_ALIGNED(width, 32)) {
  256. SplitUVRow = SplitUVRow_AVX2;
  257. }
  258. }
  259. #endif
  260. #if defined(HAS_SPLITUVROW_NEON)
  261. if (TestCpuFlag(kCpuHasNEON)) {
  262. SplitUVRow = SplitUVRow_Any_NEON;
  263. if (IS_ALIGNED(width, 16)) {
  264. SplitUVRow = SplitUVRow_NEON;
  265. }
  266. }
  267. #endif
  268. #if defined(HAS_SPLITUVROW_DSPR2)
  269. if (TestCpuFlag(kCpuHasDSPR2) &&
  270. IS_ALIGNED(dst_u, 4) && IS_ALIGNED(dst_stride_u, 4) &&
  271. IS_ALIGNED(dst_v, 4) && IS_ALIGNED(dst_stride_v, 4)) {
  272. SplitUVRow = SplitUVRow_Any_DSPR2;
  273. if (IS_ALIGNED(width, 16)) {
  274. SplitUVRow = SplitUVRow_DSPR2;
  275. }
  276. }
  277. #endif
  278. for (y = 0; y < height; ++y) {
  279. // Copy a row of UV.
  280. SplitUVRow(src_uv, dst_u, dst_v, width);
  281. dst_u += dst_stride_u;
  282. dst_v += dst_stride_v;
  283. src_uv += src_stride_uv;
  284. }
  285. }
  286. LIBYUV_API
  287. void MergeUVPlane(const uint8* src_u, int src_stride_u,
  288. const uint8* src_v, int src_stride_v,
  289. uint8* dst_uv, int dst_stride_uv,
  290. int width, int height) {
  291. int y;
  292. void (*MergeUVRow)(const uint8* src_u, const uint8* src_v, uint8* dst_uv,
  293. int width) = MergeUVRow_C;
  294. // Coalesce rows.
  295. // Negative height means invert the image.
  296. if (height < 0) {
  297. height = -height;
  298. dst_uv = dst_uv + (height - 1) * dst_stride_uv;
  299. dst_stride_uv = -dst_stride_uv;
  300. }
  301. // Coalesce rows.
  302. if (src_stride_u == width &&
  303. src_stride_v == width &&
  304. dst_stride_uv == width * 2) {
  305. width *= height;
  306. height = 1;
  307. src_stride_u = src_stride_v = dst_stride_uv = 0;
  308. }
  309. #if defined(HAS_MERGEUVROW_SSE2)
  310. if (TestCpuFlag(kCpuHasSSE2)) {
  311. MergeUVRow = MergeUVRow_Any_SSE2;
  312. if (IS_ALIGNED(width, 16)) {
  313. MergeUVRow = MergeUVRow_SSE2;
  314. }
  315. }
  316. #endif
  317. #if defined(HAS_MERGEUVROW_AVX2)
  318. if (TestCpuFlag(kCpuHasAVX2)) {
  319. MergeUVRow = MergeUVRow_Any_AVX2;
  320. if (IS_ALIGNED(width, 32)) {
  321. MergeUVRow = MergeUVRow_AVX2;
  322. }
  323. }
  324. #endif
  325. #if defined(HAS_MERGEUVROW_NEON)
  326. if (TestCpuFlag(kCpuHasNEON)) {
  327. MergeUVRow = MergeUVRow_Any_NEON;
  328. if (IS_ALIGNED(width, 16)) {
  329. MergeUVRow = MergeUVRow_NEON;
  330. }
  331. }
  332. #endif
  333. for (y = 0; y < height; ++y) {
  334. // Merge a row of U and V into a row of UV.
  335. MergeUVRow(src_u, src_v, dst_uv, width);
  336. src_u += src_stride_u;
  337. src_v += src_stride_v;
  338. dst_uv += dst_stride_uv;
  339. }
  340. }
  341. // Mirror a plane of data.
  342. void MirrorPlane(const uint8* src_y, int src_stride_y,
  343. uint8* dst_y, int dst_stride_y,
  344. int width, int height) {
  345. int y;
  346. void (*MirrorRow)(const uint8* src, uint8* dst, int width) = MirrorRow_C;
  347. // Negative height means invert the image.
  348. if (height < 0) {
  349. height = -height;
  350. src_y = src_y + (height - 1) * src_stride_y;
  351. src_stride_y = -src_stride_y;
  352. }
  353. #if defined(HAS_MIRRORROW_NEON)
  354. if (TestCpuFlag(kCpuHasNEON)) {
  355. MirrorRow = MirrorRow_Any_NEON;
  356. if (IS_ALIGNED(width, 16)) {
  357. MirrorRow = MirrorRow_NEON;
  358. }
  359. }
  360. #endif
  361. #if defined(HAS_MIRRORROW_SSSE3)
  362. if (TestCpuFlag(kCpuHasSSSE3)) {
  363. MirrorRow = MirrorRow_Any_SSSE3;
  364. if (IS_ALIGNED(width, 16)) {
  365. MirrorRow = MirrorRow_SSSE3;
  366. }
  367. }
  368. #endif
  369. #if defined(HAS_MIRRORROW_AVX2)
  370. if (TestCpuFlag(kCpuHasAVX2)) {
  371. MirrorRow = MirrorRow_Any_AVX2;
  372. if (IS_ALIGNED(width, 32)) {
  373. MirrorRow = MirrorRow_AVX2;
  374. }
  375. }
  376. #endif
  377. // TODO(fbarchard): Mirror on mips handle unaligned memory.
  378. #if defined(HAS_MIRRORROW_DSPR2)
  379. if (TestCpuFlag(kCpuHasDSPR2) &&
  380. IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) &&
  381. IS_ALIGNED(dst_y, 4) && IS_ALIGNED(dst_stride_y, 4)) {
  382. MirrorRow = MirrorRow_DSPR2;
  383. }
  384. #endif
  385. // Mirror plane
  386. for (y = 0; y < height; ++y) {
  387. MirrorRow(src_y, dst_y, width);
  388. src_y += src_stride_y;
  389. dst_y += dst_stride_y;
  390. }
  391. }
  392. // Convert YUY2 to I422.
  393. LIBYUV_API
  394. int YUY2ToI422(const uint8* src_yuy2, int src_stride_yuy2,
  395. uint8* dst_y, int dst_stride_y,
  396. uint8* dst_u, int dst_stride_u,
  397. uint8* dst_v, int dst_stride_v,
  398. int width, int height) {
  399. int y;
  400. void (*YUY2ToUV422Row)(const uint8* src_yuy2,
  401. uint8* dst_u, uint8* dst_v, int width) =
  402. YUY2ToUV422Row_C;
  403. void (*YUY2ToYRow)(const uint8* src_yuy2, uint8* dst_y, int width) =
  404. YUY2ToYRow_C;
  405. // Negative height means invert the image.
  406. if (height < 0) {
  407. height = -height;
  408. src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
  409. src_stride_yuy2 = -src_stride_yuy2;
  410. }
  411. // Coalesce rows.
  412. if (src_stride_yuy2 == width * 2 &&
  413. dst_stride_y == width &&
  414. dst_stride_u * 2 == width &&
  415. dst_stride_v * 2 == width) {
  416. width *= height;
  417. height = 1;
  418. src_stride_yuy2 = dst_stride_y = dst_stride_u = dst_stride_v = 0;
  419. }
  420. #if defined(HAS_YUY2TOYROW_SSE2)
  421. if (TestCpuFlag(kCpuHasSSE2)) {
  422. YUY2ToUV422Row = YUY2ToUV422Row_Any_SSE2;
  423. YUY2ToYRow = YUY2ToYRow_Any_SSE2;
  424. if (IS_ALIGNED(width, 16)) {
  425. YUY2ToUV422Row = YUY2ToUV422Row_SSE2;
  426. YUY2ToYRow = YUY2ToYRow_SSE2;
  427. }
  428. }
  429. #endif
  430. #if defined(HAS_YUY2TOYROW_AVX2)
  431. if (TestCpuFlag(kCpuHasAVX2)) {
  432. YUY2ToUV422Row = YUY2ToUV422Row_Any_AVX2;
  433. YUY2ToYRow = YUY2ToYRow_Any_AVX2;
  434. if (IS_ALIGNED(width, 32)) {
  435. YUY2ToUV422Row = YUY2ToUV422Row_AVX2;
  436. YUY2ToYRow = YUY2ToYRow_AVX2;
  437. }
  438. }
  439. #endif
  440. #if defined(HAS_YUY2TOYROW_NEON)
  441. if (TestCpuFlag(kCpuHasNEON)) {
  442. YUY2ToYRow = YUY2ToYRow_Any_NEON;
  443. if (width >= 16) {
  444. YUY2ToUV422Row = YUY2ToUV422Row_Any_NEON;
  445. }
  446. if (IS_ALIGNED(width, 16)) {
  447. YUY2ToYRow = YUY2ToYRow_NEON;
  448. YUY2ToUV422Row = YUY2ToUV422Row_NEON;
  449. }
  450. }
  451. #endif
  452. for (y = 0; y < height; ++y) {
  453. YUY2ToUV422Row(src_yuy2, dst_u, dst_v, width);
  454. YUY2ToYRow(src_yuy2, dst_y, width);
  455. src_yuy2 += src_stride_yuy2;
  456. dst_y += dst_stride_y;
  457. dst_u += dst_stride_u;
  458. dst_v += dst_stride_v;
  459. }
  460. return 0;
  461. }
  462. // Convert UYVY to I422.
  463. LIBYUV_API
  464. int UYVYToI422(const uint8* src_uyvy, int src_stride_uyvy,
  465. uint8* dst_y, int dst_stride_y,
  466. uint8* dst_u, int dst_stride_u,
  467. uint8* dst_v, int dst_stride_v,
  468. int width, int height) {
  469. int y;
  470. void (*UYVYToUV422Row)(const uint8* src_uyvy,
  471. uint8* dst_u, uint8* dst_v, int width) =
  472. UYVYToUV422Row_C;
  473. void (*UYVYToYRow)(const uint8* src_uyvy,
  474. uint8* dst_y, int width) = UYVYToYRow_C;
  475. // Negative height means invert the image.
  476. if (height < 0) {
  477. height = -height;
  478. src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
  479. src_stride_uyvy = -src_stride_uyvy;
  480. }
  481. // Coalesce rows.
  482. if (src_stride_uyvy == width * 2 &&
  483. dst_stride_y == width &&
  484. dst_stride_u * 2 == width &&
  485. dst_stride_v * 2 == width) {
  486. width *= height;
  487. height = 1;
  488. src_stride_uyvy = dst_stride_y = dst_stride_u = dst_stride_v = 0;
  489. }
  490. #if defined(HAS_UYVYTOYROW_SSE2)
  491. if (TestCpuFlag(kCpuHasSSE2)) {
  492. UYVYToUV422Row = UYVYToUV422Row_Any_SSE2;
  493. UYVYToYRow = UYVYToYRow_Any_SSE2;
  494. if (IS_ALIGNED(width, 16)) {
  495. UYVYToUV422Row = UYVYToUV422Row_SSE2;
  496. UYVYToYRow = UYVYToYRow_SSE2;
  497. }
  498. }
  499. #endif
  500. #if defined(HAS_UYVYTOYROW_AVX2)
  501. if (TestCpuFlag(kCpuHasAVX2)) {
  502. UYVYToUV422Row = UYVYToUV422Row_Any_AVX2;
  503. UYVYToYRow = UYVYToYRow_Any_AVX2;
  504. if (IS_ALIGNED(width, 32)) {
  505. UYVYToUV422Row = UYVYToUV422Row_AVX2;
  506. UYVYToYRow = UYVYToYRow_AVX2;
  507. }
  508. }
  509. #endif
  510. #if defined(HAS_UYVYTOYROW_NEON)
  511. if (TestCpuFlag(kCpuHasNEON)) {
  512. UYVYToYRow = UYVYToYRow_Any_NEON;
  513. if (width >= 16) {
  514. UYVYToUV422Row = UYVYToUV422Row_Any_NEON;
  515. }
  516. if (IS_ALIGNED(width, 16)) {
  517. UYVYToYRow = UYVYToYRow_NEON;
  518. UYVYToUV422Row = UYVYToUV422Row_NEON;
  519. }
  520. }
  521. #endif
  522. for (y = 0; y < height; ++y) {
  523. UYVYToUV422Row(src_uyvy, dst_u, dst_v, width);
  524. UYVYToYRow(src_uyvy, dst_y, width);
  525. src_uyvy += src_stride_uyvy;
  526. dst_y += dst_stride_y;
  527. dst_u += dst_stride_u;
  528. dst_v += dst_stride_v;
  529. }
  530. return 0;
  531. }
  532. // Mirror I400 with optional flipping
  533. LIBYUV_API
  534. int I400Mirror(const uint8* src_y, int src_stride_y,
  535. uint8* dst_y, int dst_stride_y,
  536. int width, int height) {
  537. if (!src_y || !dst_y ||
  538. width <= 0 || height == 0) {
  539. return -1;
  540. }
  541. // Negative height means invert the image.
  542. if (height < 0) {
  543. height = -height;
  544. src_y = src_y + (height - 1) * src_stride_y;
  545. src_stride_y = -src_stride_y;
  546. }
  547. MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  548. return 0;
  549. }
  550. // Mirror I420 with optional flipping
  551. LIBYUV_API
  552. int I420Mirror(const uint8* src_y, int src_stride_y,
  553. const uint8* src_u, int src_stride_u,
  554. const uint8* src_v, int src_stride_v,
  555. uint8* dst_y, int dst_stride_y,
  556. uint8* dst_u, int dst_stride_u,
  557. uint8* dst_v, int dst_stride_v,
  558. int width, int height) {
  559. int halfwidth = (width + 1) >> 1;
  560. int halfheight = (height + 1) >> 1;
  561. if (!src_y || !src_u || !src_v || !dst_y || !dst_u || !dst_v ||
  562. width <= 0 || height == 0) {
  563. return -1;
  564. }
  565. // Negative height means invert the image.
  566. if (height < 0) {
  567. height = -height;
  568. halfheight = (height + 1) >> 1;
  569. src_y = src_y + (height - 1) * src_stride_y;
  570. src_u = src_u + (halfheight - 1) * src_stride_u;
  571. src_v = src_v + (halfheight - 1) * src_stride_v;
  572. src_stride_y = -src_stride_y;
  573. src_stride_u = -src_stride_u;
  574. src_stride_v = -src_stride_v;
  575. }
  576. if (dst_y) {
  577. MirrorPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
  578. }
  579. MirrorPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
  580. MirrorPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
  581. return 0;
  582. }
  583. // ARGB mirror.
  584. LIBYUV_API
  585. int ARGBMirror(const uint8* src_argb, int src_stride_argb,
  586. uint8* dst_argb, int dst_stride_argb,
  587. int width, int height) {
  588. int y;
  589. void (*ARGBMirrorRow)(const uint8* src, uint8* dst, int width) =
  590. ARGBMirrorRow_C;
  591. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  592. return -1;
  593. }
  594. // Negative height means invert the image.
  595. if (height < 0) {
  596. height = -height;
  597. src_argb = src_argb + (height - 1) * src_stride_argb;
  598. src_stride_argb = -src_stride_argb;
  599. }
  600. #if defined(HAS_ARGBMIRRORROW_NEON)
  601. if (TestCpuFlag(kCpuHasNEON)) {
  602. ARGBMirrorRow = ARGBMirrorRow_Any_NEON;
  603. if (IS_ALIGNED(width, 4)) {
  604. ARGBMirrorRow = ARGBMirrorRow_NEON;
  605. }
  606. }
  607. #endif
  608. #if defined(HAS_ARGBMIRRORROW_SSE2)
  609. if (TestCpuFlag(kCpuHasSSE2)) {
  610. ARGBMirrorRow = ARGBMirrorRow_Any_SSE2;
  611. if (IS_ALIGNED(width, 4)) {
  612. ARGBMirrorRow = ARGBMirrorRow_SSE2;
  613. }
  614. }
  615. #endif
  616. #if defined(HAS_ARGBMIRRORROW_AVX2)
  617. if (TestCpuFlag(kCpuHasAVX2)) {
  618. ARGBMirrorRow = ARGBMirrorRow_Any_AVX2;
  619. if (IS_ALIGNED(width, 8)) {
  620. ARGBMirrorRow = ARGBMirrorRow_AVX2;
  621. }
  622. }
  623. #endif
  624. // Mirror plane
  625. for (y = 0; y < height; ++y) {
  626. ARGBMirrorRow(src_argb, dst_argb, width);
  627. src_argb += src_stride_argb;
  628. dst_argb += dst_stride_argb;
  629. }
  630. return 0;
  631. }
  632. // Get a blender that optimized for the CPU and pixel count.
  633. // As there are 6 blenders to choose from, the caller should try to use
  634. // the same blend function for all pixels if possible.
  635. LIBYUV_API
  636. ARGBBlendRow GetARGBBlend() {
  637. void (*ARGBBlendRow)(const uint8* src_argb, const uint8* src_argb1,
  638. uint8* dst_argb, int width) = ARGBBlendRow_C;
  639. #if defined(HAS_ARGBBLENDROW_SSSE3)
  640. if (TestCpuFlag(kCpuHasSSSE3)) {
  641. ARGBBlendRow = ARGBBlendRow_SSSE3;
  642. return ARGBBlendRow;
  643. }
  644. #endif
  645. #if defined(HAS_ARGBBLENDROW_NEON)
  646. if (TestCpuFlag(kCpuHasNEON)) {
  647. ARGBBlendRow = ARGBBlendRow_NEON;
  648. }
  649. #endif
  650. return ARGBBlendRow;
  651. }
  652. // Alpha Blend 2 ARGB images and store to destination.
  653. LIBYUV_API
  654. int ARGBBlend(const uint8* src_argb0, int src_stride_argb0,
  655. const uint8* src_argb1, int src_stride_argb1,
  656. uint8* dst_argb, int dst_stride_argb,
  657. int width, int height) {
  658. int y;
  659. void (*ARGBBlendRow)(const uint8* src_argb, const uint8* src_argb1,
  660. uint8* dst_argb, int width) = GetARGBBlend();
  661. if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
  662. return -1;
  663. }
  664. // Negative height means invert the image.
  665. if (height < 0) {
  666. height = -height;
  667. dst_argb = dst_argb + (height - 1) * dst_stride_argb;
  668. dst_stride_argb = -dst_stride_argb;
  669. }
  670. // Coalesce rows.
  671. if (src_stride_argb0 == width * 4 &&
  672. src_stride_argb1 == width * 4 &&
  673. dst_stride_argb == width * 4) {
  674. width *= height;
  675. height = 1;
  676. src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
  677. }
  678. for (y = 0; y < height; ++y) {
  679. ARGBBlendRow(src_argb0, src_argb1, dst_argb, width);
  680. src_argb0 += src_stride_argb0;
  681. src_argb1 += src_stride_argb1;
  682. dst_argb += dst_stride_argb;
  683. }
  684. return 0;
  685. }
  686. // Alpha Blend plane and store to destination.
  687. LIBYUV_API
  688. int BlendPlane(const uint8* src_y0, int src_stride_y0,
  689. const uint8* src_y1, int src_stride_y1,
  690. const uint8* alpha, int alpha_stride,
  691. uint8* dst_y, int dst_stride_y,
  692. int width, int height) {
  693. int y;
  694. void (*BlendPlaneRow)(const uint8* src0, const uint8* src1,
  695. const uint8* alpha, uint8* dst, int width) = BlendPlaneRow_C;
  696. if (!src_y0 || !src_y1 || !alpha || !dst_y || width <= 0 || height == 0) {
  697. return -1;
  698. }
  699. // Negative height means invert the image.
  700. if (height < 0) {
  701. height = -height;
  702. dst_y = dst_y + (height - 1) * dst_stride_y;
  703. dst_stride_y = -dst_stride_y;
  704. }
  705. // Coalesce rows for Y plane.
  706. if (src_stride_y0 == width &&
  707. src_stride_y1 == width &&
  708. alpha_stride == width &&
  709. dst_stride_y == width) {
  710. width *= height;
  711. height = 1;
  712. src_stride_y0 = src_stride_y1 = alpha_stride = dst_stride_y = 0;
  713. }
  714. #if defined(HAS_BLENDPLANEROW_SSSE3)
  715. if (TestCpuFlag(kCpuHasSSSE3)) {
  716. BlendPlaneRow = BlendPlaneRow_Any_SSSE3;
  717. if (IS_ALIGNED(width, 8)) {
  718. BlendPlaneRow = BlendPlaneRow_SSSE3;
  719. }
  720. }
  721. #endif
  722. #if defined(HAS_BLENDPLANEROW_AVX2)
  723. if (TestCpuFlag(kCpuHasAVX2)) {
  724. BlendPlaneRow = BlendPlaneRow_Any_AVX2;
  725. if (IS_ALIGNED(width, 32)) {
  726. BlendPlaneRow = BlendPlaneRow_AVX2;
  727. }
  728. }
  729. #endif
  730. for (y = 0; y < height; ++y) {
  731. BlendPlaneRow(src_y0, src_y1, alpha, dst_y, width);
  732. src_y0 += src_stride_y0;
  733. src_y1 += src_stride_y1;
  734. alpha += alpha_stride;
  735. dst_y += dst_stride_y;
  736. }
  737. return 0;
  738. }
  739. #define MAXTWIDTH 2048
  740. // Alpha Blend YUV images and store to destination.
  741. LIBYUV_API
  742. int I420Blend(const uint8* src_y0, int src_stride_y0,
  743. const uint8* src_u0, int src_stride_u0,
  744. const uint8* src_v0, int src_stride_v0,
  745. const uint8* src_y1, int src_stride_y1,
  746. const uint8* src_u1, int src_stride_u1,
  747. const uint8* src_v1, int src_stride_v1,
  748. const uint8* alpha, int alpha_stride,
  749. uint8* dst_y, int dst_stride_y,
  750. uint8* dst_u, int dst_stride_u,
  751. uint8* dst_v, int dst_stride_v,
  752. int width, int height) {
  753. int y;
  754. // Half width/height for UV.
  755. int halfwidth = (width + 1) >> 1;
  756. void (*BlendPlaneRow)(const uint8* src0, const uint8* src1,
  757. const uint8* alpha, uint8* dst, int width) = BlendPlaneRow_C;
  758. void (*ScaleRowDown2)(const uint8* src_ptr, ptrdiff_t src_stride,
  759. uint8* dst_ptr, int dst_width) = ScaleRowDown2Box_C;
  760. if (!src_y0 || !src_u0 || !src_v0 || !src_y1 || !src_u1 || !src_v1 ||
  761. !alpha || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
  762. return -1;
  763. }
  764. // Negative height means invert the image.
  765. if (height < 0) {
  766. height = -height;
  767. dst_y = dst_y + (height - 1) * dst_stride_y;
  768. dst_stride_y = -dst_stride_y;
  769. }
  770. // Blend Y plane.
  771. BlendPlane(src_y0, src_stride_y0,
  772. src_y1, src_stride_y1,
  773. alpha, alpha_stride,
  774. dst_y, dst_stride_y,
  775. width, height);
  776. #if defined(HAS_BLENDPLANEROW_SSSE3)
  777. if (TestCpuFlag(kCpuHasSSSE3)) {
  778. BlendPlaneRow = BlendPlaneRow_Any_SSSE3;
  779. if (IS_ALIGNED(halfwidth, 8)) {
  780. BlendPlaneRow = BlendPlaneRow_SSSE3;
  781. }
  782. }
  783. #endif
  784. #if defined(HAS_BLENDPLANEROW_AVX2)
  785. if (TestCpuFlag(kCpuHasAVX2)) {
  786. BlendPlaneRow = BlendPlaneRow_Any_AVX2;
  787. if (IS_ALIGNED(halfwidth, 32)) {
  788. BlendPlaneRow = BlendPlaneRow_AVX2;
  789. }
  790. }
  791. #endif
  792. if (!IS_ALIGNED(width, 2)) {
  793. ScaleRowDown2 = ScaleRowDown2Box_Odd_C;
  794. }
  795. #if defined(HAS_SCALEROWDOWN2_NEON)
  796. if (TestCpuFlag(kCpuHasNEON)) {
  797. ScaleRowDown2 = ScaleRowDown2Box_Odd_NEON;
  798. if (IS_ALIGNED(width, 2)) {
  799. ScaleRowDown2 = ScaleRowDown2Box_Any_NEON;
  800. if (IS_ALIGNED(halfwidth, 16)) {
  801. ScaleRowDown2 = ScaleRowDown2Box_NEON;
  802. }
  803. }
  804. }
  805. #endif
  806. #if defined(HAS_SCALEROWDOWN2_SSSE3)
  807. if (TestCpuFlag(kCpuHasSSSE3)) {
  808. ScaleRowDown2 = ScaleRowDown2Box_Odd_SSSE3;
  809. if (IS_ALIGNED(width, 2)) {
  810. ScaleRowDown2 = ScaleRowDown2Box_Any_SSSE3;
  811. if (IS_ALIGNED(halfwidth, 16)) {
  812. ScaleRowDown2 = ScaleRowDown2Box_SSSE3;
  813. }
  814. }
  815. }
  816. #endif
  817. #if defined(HAS_SCALEROWDOWN2_AVX2)
  818. if (TestCpuFlag(kCpuHasAVX2)) {
  819. ScaleRowDown2 = ScaleRowDown2Box_Odd_AVX2;
  820. if (IS_ALIGNED(width, 2)) {
  821. ScaleRowDown2 = ScaleRowDown2Box_Any_AVX2;
  822. if (IS_ALIGNED(halfwidth, 32)) {
  823. ScaleRowDown2 = ScaleRowDown2Box_AVX2;
  824. }
  825. }
  826. }
  827. #endif
  828. // Row buffer for intermediate alpha pixels.
  829. align_buffer_64(halfalpha, halfwidth);
  830. for (y = 0; y < height; y += 2) {
  831. // last row of odd height image use 1 row of alpha instead of 2.
  832. if (y == (height - 1)) {
  833. alpha_stride = 0;
  834. }
  835. // Subsample 2 rows of UV to half width and half height.
  836. ScaleRowDown2(alpha, alpha_stride, halfalpha, halfwidth);
  837. alpha += alpha_stride * 2;
  838. BlendPlaneRow(src_u0, src_u1, halfalpha, dst_u, halfwidth);
  839. BlendPlaneRow(src_v0, src_v1, halfalpha, dst_v, halfwidth);
  840. src_u0 += src_stride_u0;
  841. src_u1 += src_stride_u1;
  842. dst_u += dst_stride_u;
  843. src_v0 += src_stride_v0;
  844. src_v1 += src_stride_v1;
  845. dst_v += dst_stride_v;
  846. }
  847. free_aligned_buffer_64(halfalpha);
  848. return 0;
  849. }
  850. // Multiply 2 ARGB images and store to destination.
  851. LIBYUV_API
  852. int ARGBMultiply(const uint8* src_argb0, int src_stride_argb0,
  853. const uint8* src_argb1, int src_stride_argb1,
  854. uint8* dst_argb, int dst_stride_argb,
  855. int width, int height) {
  856. int y;
  857. void (*ARGBMultiplyRow)(const uint8* src0, const uint8* src1, uint8* dst,
  858. int width) = ARGBMultiplyRow_C;
  859. if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
  860. return -1;
  861. }
  862. // Negative height means invert the image.
  863. if (height < 0) {
  864. height = -height;
  865. dst_argb = dst_argb + (height - 1) * dst_stride_argb;
  866. dst_stride_argb = -dst_stride_argb;
  867. }
  868. // Coalesce rows.
  869. if (src_stride_argb0 == width * 4 &&
  870. src_stride_argb1 == width * 4 &&
  871. dst_stride_argb == width * 4) {
  872. width *= height;
  873. height = 1;
  874. src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
  875. }
  876. #if defined(HAS_ARGBMULTIPLYROW_SSE2)
  877. if (TestCpuFlag(kCpuHasSSE2)) {
  878. ARGBMultiplyRow = ARGBMultiplyRow_Any_SSE2;
  879. if (IS_ALIGNED(width, 4)) {
  880. ARGBMultiplyRow = ARGBMultiplyRow_SSE2;
  881. }
  882. }
  883. #endif
  884. #if defined(HAS_ARGBMULTIPLYROW_AVX2)
  885. if (TestCpuFlag(kCpuHasAVX2)) {
  886. ARGBMultiplyRow = ARGBMultiplyRow_Any_AVX2;
  887. if (IS_ALIGNED(width, 8)) {
  888. ARGBMultiplyRow = ARGBMultiplyRow_AVX2;
  889. }
  890. }
  891. #endif
  892. #if defined(HAS_ARGBMULTIPLYROW_NEON)
  893. if (TestCpuFlag(kCpuHasNEON)) {
  894. ARGBMultiplyRow = ARGBMultiplyRow_Any_NEON;
  895. if (IS_ALIGNED(width, 8)) {
  896. ARGBMultiplyRow = ARGBMultiplyRow_NEON;
  897. }
  898. }
  899. #endif
  900. // Multiply plane
  901. for (y = 0; y < height; ++y) {
  902. ARGBMultiplyRow(src_argb0, src_argb1, dst_argb, width);
  903. src_argb0 += src_stride_argb0;
  904. src_argb1 += src_stride_argb1;
  905. dst_argb += dst_stride_argb;
  906. }
  907. return 0;
  908. }
  909. // Add 2 ARGB images and store to destination.
  910. LIBYUV_API
  911. int ARGBAdd(const uint8* src_argb0, int src_stride_argb0,
  912. const uint8* src_argb1, int src_stride_argb1,
  913. uint8* dst_argb, int dst_stride_argb,
  914. int width, int height) {
  915. int y;
  916. void (*ARGBAddRow)(const uint8* src0, const uint8* src1, uint8* dst,
  917. int width) = ARGBAddRow_C;
  918. if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
  919. return -1;
  920. }
  921. // Negative height means invert the image.
  922. if (height < 0) {
  923. height = -height;
  924. dst_argb = dst_argb + (height - 1) * dst_stride_argb;
  925. dst_stride_argb = -dst_stride_argb;
  926. }
  927. // Coalesce rows.
  928. if (src_stride_argb0 == width * 4 &&
  929. src_stride_argb1 == width * 4 &&
  930. dst_stride_argb == width * 4) {
  931. width *= height;
  932. height = 1;
  933. src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
  934. }
  935. #if defined(HAS_ARGBADDROW_SSE2) && (defined(_MSC_VER) && !defined(__clang__))
  936. if (TestCpuFlag(kCpuHasSSE2)) {
  937. ARGBAddRow = ARGBAddRow_SSE2;
  938. }
  939. #endif
  940. #if defined(HAS_ARGBADDROW_SSE2) && !(defined(_MSC_VER) && !defined(__clang__))
  941. if (TestCpuFlag(kCpuHasSSE2)) {
  942. ARGBAddRow = ARGBAddRow_Any_SSE2;
  943. if (IS_ALIGNED(width, 4)) {
  944. ARGBAddRow = ARGBAddRow_SSE2;
  945. }
  946. }
  947. #endif
  948. #if defined(HAS_ARGBADDROW_AVX2)
  949. if (TestCpuFlag(kCpuHasAVX2)) {
  950. ARGBAddRow = ARGBAddRow_Any_AVX2;
  951. if (IS_ALIGNED(width, 8)) {
  952. ARGBAddRow = ARGBAddRow_AVX2;
  953. }
  954. }
  955. #endif
  956. #if defined(HAS_ARGBADDROW_NEON)
  957. if (TestCpuFlag(kCpuHasNEON)) {
  958. ARGBAddRow = ARGBAddRow_Any_NEON;
  959. if (IS_ALIGNED(width, 8)) {
  960. ARGBAddRow = ARGBAddRow_NEON;
  961. }
  962. }
  963. #endif
  964. // Add plane
  965. for (y = 0; y < height; ++y) {
  966. ARGBAddRow(src_argb0, src_argb1, dst_argb, width);
  967. src_argb0 += src_stride_argb0;
  968. src_argb1 += src_stride_argb1;
  969. dst_argb += dst_stride_argb;
  970. }
  971. return 0;
  972. }
  973. // Subtract 2 ARGB images and store to destination.
  974. LIBYUV_API
  975. int ARGBSubtract(const uint8* src_argb0, int src_stride_argb0,
  976. const uint8* src_argb1, int src_stride_argb1,
  977. uint8* dst_argb, int dst_stride_argb,
  978. int width, int height) {
  979. int y;
  980. void (*ARGBSubtractRow)(const uint8* src0, const uint8* src1, uint8* dst,
  981. int width) = ARGBSubtractRow_C;
  982. if (!src_argb0 || !src_argb1 || !dst_argb || width <= 0 || height == 0) {
  983. return -1;
  984. }
  985. // Negative height means invert the image.
  986. if (height < 0) {
  987. height = -height;
  988. dst_argb = dst_argb + (height - 1) * dst_stride_argb;
  989. dst_stride_argb = -dst_stride_argb;
  990. }
  991. // Coalesce rows.
  992. if (src_stride_argb0 == width * 4 &&
  993. src_stride_argb1 == width * 4 &&
  994. dst_stride_argb == width * 4) {
  995. width *= height;
  996. height = 1;
  997. src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
  998. }
  999. #if defined(HAS_ARGBSUBTRACTROW_SSE2)
  1000. if (TestCpuFlag(kCpuHasSSE2)) {
  1001. ARGBSubtractRow = ARGBSubtractRow_Any_SSE2;
  1002. if (IS_ALIGNED(width, 4)) {
  1003. ARGBSubtractRow = ARGBSubtractRow_SSE2;
  1004. }
  1005. }
  1006. #endif
  1007. #if defined(HAS_ARGBSUBTRACTROW_AVX2)
  1008. if (TestCpuFlag(kCpuHasAVX2)) {
  1009. ARGBSubtractRow = ARGBSubtractRow_Any_AVX2;
  1010. if (IS_ALIGNED(width, 8)) {
  1011. ARGBSubtractRow = ARGBSubtractRow_AVX2;
  1012. }
  1013. }
  1014. #endif
  1015. #if defined(HAS_ARGBSUBTRACTROW_NEON)
  1016. if (TestCpuFlag(kCpuHasNEON)) {
  1017. ARGBSubtractRow = ARGBSubtractRow_Any_NEON;
  1018. if (IS_ALIGNED(width, 8)) {
  1019. ARGBSubtractRow = ARGBSubtractRow_NEON;
  1020. }
  1021. }
  1022. #endif
  1023. // Subtract plane
  1024. for (y = 0; y < height; ++y) {
  1025. ARGBSubtractRow(src_argb0, src_argb1, dst_argb, width);
  1026. src_argb0 += src_stride_argb0;
  1027. src_argb1 += src_stride_argb1;
  1028. dst_argb += dst_stride_argb;
  1029. }
  1030. return 0;
  1031. }
  1032. // Convert I422 to RGBA with matrix
  1033. static int I422ToRGBAMatrix(const uint8* src_y, int src_stride_y,
  1034. const uint8* src_u, int src_stride_u,
  1035. const uint8* src_v, int src_stride_v,
  1036. uint8* dst_rgba, int dst_stride_rgba,
  1037. const struct YuvConstants* yuvconstants,
  1038. int width, int height) {
  1039. int y;
  1040. void (*I422ToRGBARow)(const uint8* y_buf,
  1041. const uint8* u_buf,
  1042. const uint8* v_buf,
  1043. uint8* rgb_buf,
  1044. const struct YuvConstants* yuvconstants,
  1045. int width) = I422ToRGBARow_C;
  1046. if (!src_y || !src_u || !src_v || !dst_rgba ||
  1047. width <= 0 || height == 0) {
  1048. return -1;
  1049. }
  1050. // Negative height means invert the image.
  1051. if (height < 0) {
  1052. height = -height;
  1053. dst_rgba = dst_rgba + (height - 1) * dst_stride_rgba;
  1054. dst_stride_rgba = -dst_stride_rgba;
  1055. }
  1056. #if defined(HAS_I422TORGBAROW_SSSE3)
  1057. if (TestCpuFlag(kCpuHasSSSE3)) {
  1058. I422ToRGBARow = I422ToRGBARow_Any_SSSE3;
  1059. if (IS_ALIGNED(width, 8)) {
  1060. I422ToRGBARow = I422ToRGBARow_SSSE3;
  1061. }
  1062. }
  1063. #endif
  1064. #if defined(HAS_I422TORGBAROW_AVX2)
  1065. if (TestCpuFlag(kCpuHasAVX2)) {
  1066. I422ToRGBARow = I422ToRGBARow_Any_AVX2;
  1067. if (IS_ALIGNED(width, 16)) {
  1068. I422ToRGBARow = I422ToRGBARow_AVX2;
  1069. }
  1070. }
  1071. #endif
  1072. #if defined(HAS_I422TORGBAROW_NEON)
  1073. if (TestCpuFlag(kCpuHasNEON)) {
  1074. I422ToRGBARow = I422ToRGBARow_Any_NEON;
  1075. if (IS_ALIGNED(width, 8)) {
  1076. I422ToRGBARow = I422ToRGBARow_NEON;
  1077. }
  1078. }
  1079. #endif
  1080. #if defined(HAS_I422TORGBAROW_DSPR2)
  1081. if (TestCpuFlag(kCpuHasDSPR2) && IS_ALIGNED(width, 4) &&
  1082. IS_ALIGNED(src_y, 4) && IS_ALIGNED(src_stride_y, 4) &&
  1083. IS_ALIGNED(src_u, 2) && IS_ALIGNED(src_stride_u, 2) &&
  1084. IS_ALIGNED(src_v, 2) && IS_ALIGNED(src_stride_v, 2) &&
  1085. IS_ALIGNED(dst_rgba, 4) && IS_ALIGNED(dst_stride_rgba, 4)) {
  1086. I422ToRGBARow = I422ToRGBARow_DSPR2;
  1087. }
  1088. #endif
  1089. for (y = 0; y < height; ++y) {
  1090. I422ToRGBARow(src_y, src_u, src_v, dst_rgba, yuvconstants, width);
  1091. dst_rgba += dst_stride_rgba;
  1092. src_y += src_stride_y;
  1093. src_u += src_stride_u;
  1094. src_v += src_stride_v;
  1095. }
  1096. return 0;
  1097. }
  1098. // Convert I422 to RGBA.
  1099. LIBYUV_API
  1100. int I422ToRGBA(const uint8* src_y, int src_stride_y,
  1101. const uint8* src_u, int src_stride_u,
  1102. const uint8* src_v, int src_stride_v,
  1103. uint8* dst_rgba, int dst_stride_rgba,
  1104. int width, int height) {
  1105. return I422ToRGBAMatrix(src_y, src_stride_y,
  1106. src_u, src_stride_u,
  1107. src_v, src_stride_v,
  1108. dst_rgba, dst_stride_rgba,
  1109. &kYuvI601Constants,
  1110. width, height);
  1111. }
  1112. // Convert I422 to BGRA.
  1113. LIBYUV_API
  1114. int I422ToBGRA(const uint8* src_y, int src_stride_y,
  1115. const uint8* src_u, int src_stride_u,
  1116. const uint8* src_v, int src_stride_v,
  1117. uint8* dst_bgra, int dst_stride_bgra,
  1118. int width, int height) {
  1119. return I422ToRGBAMatrix(src_y, src_stride_y,
  1120. src_v, src_stride_v, // Swap U and V
  1121. src_u, src_stride_u,
  1122. dst_bgra, dst_stride_bgra,
  1123. &kYvuI601Constants, // Use Yvu matrix
  1124. width, height);
  1125. }
  1126. // Convert NV12 to RGB565.
  1127. LIBYUV_API
  1128. int NV12ToRGB565(const uint8* src_y, int src_stride_y,
  1129. const uint8* src_uv, int src_stride_uv,
  1130. uint8* dst_rgb565, int dst_stride_rgb565,
  1131. int width, int height) {
  1132. int y;
  1133. void (*NV12ToRGB565Row)(const uint8* y_buf,
  1134. const uint8* uv_buf,
  1135. uint8* rgb_buf,
  1136. const struct YuvConstants* yuvconstants,
  1137. int width) = NV12ToRGB565Row_C;
  1138. if (!src_y || !src_uv || !dst_rgb565 ||
  1139. width <= 0 || height == 0) {
  1140. return -1;
  1141. }
  1142. // Negative height means invert the image.
  1143. if (height < 0) {
  1144. height = -height;
  1145. dst_rgb565 = dst_rgb565 + (height - 1) * dst_stride_rgb565;
  1146. dst_stride_rgb565 = -dst_stride_rgb565;
  1147. }
  1148. #if defined(HAS_NV12TORGB565ROW_SSSE3)
  1149. if (TestCpuFlag(kCpuHasSSSE3)) {
  1150. NV12ToRGB565Row = NV12ToRGB565Row_Any_SSSE3;
  1151. if (IS_ALIGNED(width, 8)) {
  1152. NV12ToRGB565Row = NV12ToRGB565Row_SSSE3;
  1153. }
  1154. }
  1155. #endif
  1156. #if defined(HAS_NV12TORGB565ROW_AVX2)
  1157. if (TestCpuFlag(kCpuHasAVX2)) {
  1158. NV12ToRGB565Row = NV12ToRGB565Row_Any_AVX2;
  1159. if (IS_ALIGNED(width, 16)) {
  1160. NV12ToRGB565Row = NV12ToRGB565Row_AVX2;
  1161. }
  1162. }
  1163. #endif
  1164. #if defined(HAS_NV12TORGB565ROW_NEON)
  1165. if (TestCpuFlag(kCpuHasNEON)) {
  1166. NV12ToRGB565Row = NV12ToRGB565Row_Any_NEON;
  1167. if (IS_ALIGNED(width, 8)) {
  1168. NV12ToRGB565Row = NV12ToRGB565Row_NEON;
  1169. }
  1170. }
  1171. #endif
  1172. for (y = 0; y < height; ++y) {
  1173. NV12ToRGB565Row(src_y, src_uv, dst_rgb565, &kYuvI601Constants, width);
  1174. dst_rgb565 += dst_stride_rgb565;
  1175. src_y += src_stride_y;
  1176. if (y & 1) {
  1177. src_uv += src_stride_uv;
  1178. }
  1179. }
  1180. return 0;
  1181. }
  1182. // Convert RAW to RGB24.
  1183. LIBYUV_API
  1184. int RAWToRGB24(const uint8* src_raw, int src_stride_raw,
  1185. uint8* dst_rgb24, int dst_stride_rgb24,
  1186. int width, int height) {
  1187. int y;
  1188. void (*RAWToRGB24Row)(const uint8* src_rgb, uint8* dst_rgb24, int width) =
  1189. RAWToRGB24Row_C;
  1190. if (!src_raw || !dst_rgb24 ||
  1191. width <= 0 || height == 0) {
  1192. return -1;
  1193. }
  1194. // Negative height means invert the image.
  1195. if (height < 0) {
  1196. height = -height;
  1197. src_raw = src_raw + (height - 1) * src_stride_raw;
  1198. src_stride_raw = -src_stride_raw;
  1199. }
  1200. // Coalesce rows.
  1201. if (src_stride_raw == width * 3 &&
  1202. dst_stride_rgb24 == width * 3) {
  1203. width *= height;
  1204. height = 1;
  1205. src_stride_raw = dst_stride_rgb24 = 0;
  1206. }
  1207. #if defined(HAS_RAWTORGB24ROW_SSSE3)
  1208. if (TestCpuFlag(kCpuHasSSSE3)) {
  1209. RAWToRGB24Row = RAWToRGB24Row_Any_SSSE3;
  1210. if (IS_ALIGNED(width, 8)) {
  1211. RAWToRGB24Row = RAWToRGB24Row_SSSE3;
  1212. }
  1213. }
  1214. #endif
  1215. #if defined(HAS_RAWTORGB24ROW_NEON)
  1216. if (TestCpuFlag(kCpuHasNEON)) {
  1217. RAWToRGB24Row = RAWToRGB24Row_Any_NEON;
  1218. if (IS_ALIGNED(width, 8)) {
  1219. RAWToRGB24Row = RAWToRGB24Row_NEON;
  1220. }
  1221. }
  1222. #endif
  1223. for (y = 0; y < height; ++y) {
  1224. RAWToRGB24Row(src_raw, dst_rgb24, width);
  1225. src_raw += src_stride_raw;
  1226. dst_rgb24 += dst_stride_rgb24;
  1227. }
  1228. return 0;
  1229. }
  1230. LIBYUV_API
  1231. void SetPlane(uint8* dst_y, int dst_stride_y,
  1232. int width, int height,
  1233. uint32 value) {
  1234. int y;
  1235. void (*SetRow)(uint8* dst, uint8 value, int width) = SetRow_C;
  1236. if (height < 0) {
  1237. height = -height;
  1238. dst_y = dst_y + (height - 1) * dst_stride_y;
  1239. dst_stride_y = -dst_stride_y;
  1240. }
  1241. // Coalesce rows.
  1242. if (dst_stride_y == width) {
  1243. width *= height;
  1244. height = 1;
  1245. dst_stride_y = 0;
  1246. }
  1247. #if defined(HAS_SETROW_NEON)
  1248. if (TestCpuFlag(kCpuHasNEON)) {
  1249. SetRow = SetRow_Any_NEON;
  1250. if (IS_ALIGNED(width, 16)) {
  1251. SetRow = SetRow_NEON;
  1252. }
  1253. }
  1254. #endif
  1255. #if defined(HAS_SETROW_X86)
  1256. if (TestCpuFlag(kCpuHasX86)) {
  1257. SetRow = SetRow_Any_X86;
  1258. if (IS_ALIGNED(width, 4)) {
  1259. SetRow = SetRow_X86;
  1260. }
  1261. }
  1262. #endif
  1263. #if defined(HAS_SETROW_ERMS)
  1264. if (TestCpuFlag(kCpuHasERMS)) {
  1265. SetRow = SetRow_ERMS;
  1266. }
  1267. #endif
  1268. // Set plane
  1269. for (y = 0; y < height; ++y) {
  1270. SetRow(dst_y, value, width);
  1271. dst_y += dst_stride_y;
  1272. }
  1273. }
  1274. // Draw a rectangle into I420
  1275. LIBYUV_API
  1276. int I420Rect(uint8* dst_y, int dst_stride_y,
  1277. uint8* dst_u, int dst_stride_u,
  1278. uint8* dst_v, int dst_stride_v,
  1279. int x, int y,
  1280. int width, int height,
  1281. int value_y, int value_u, int value_v) {
  1282. int halfwidth = (width + 1) >> 1;
  1283. int halfheight = (height + 1) >> 1;
  1284. uint8* start_y = dst_y + y * dst_stride_y + x;
  1285. uint8* start_u = dst_u + (y / 2) * dst_stride_u + (x / 2);
  1286. uint8* start_v = dst_v + (y / 2) * dst_stride_v + (x / 2);
  1287. if (!dst_y || !dst_u || !dst_v ||
  1288. width <= 0 || height == 0 ||
  1289. x < 0 || y < 0 ||
  1290. value_y < 0 || value_y > 255 ||
  1291. value_u < 0 || value_u > 255 ||
  1292. value_v < 0 || value_v > 255) {
  1293. return -1;
  1294. }
  1295. SetPlane(start_y, dst_stride_y, width, height, value_y);
  1296. SetPlane(start_u, dst_stride_u, halfwidth, halfheight, value_u);
  1297. SetPlane(start_v, dst_stride_v, halfwidth, halfheight, value_v);
  1298. return 0;
  1299. }
  1300. // Draw a rectangle into ARGB
  1301. LIBYUV_API
  1302. int ARGBRect(uint8* dst_argb, int dst_stride_argb,
  1303. int dst_x, int dst_y,
  1304. int width, int height,
  1305. uint32 value) {
  1306. int y;
  1307. void (*ARGBSetRow)(uint8* dst_argb, uint32 value, int width) = ARGBSetRow_C;
  1308. if (!dst_argb ||
  1309. width <= 0 || height == 0 ||
  1310. dst_x < 0 || dst_y < 0) {
  1311. return -1;
  1312. }
  1313. if (height < 0) {
  1314. height = -height;
  1315. dst_argb = dst_argb + (height - 1) * dst_stride_argb;
  1316. dst_stride_argb = -dst_stride_argb;
  1317. }
  1318. dst_argb += dst_y * dst_stride_argb + dst_x * 4;
  1319. // Coalesce rows.
  1320. if (dst_stride_argb == width * 4) {
  1321. width *= height;
  1322. height = 1;
  1323. dst_stride_argb = 0;
  1324. }
  1325. #if defined(HAS_ARGBSETROW_NEON)
  1326. if (TestCpuFlag(kCpuHasNEON)) {
  1327. ARGBSetRow = ARGBSetRow_Any_NEON;
  1328. if (IS_ALIGNED(width, 4)) {
  1329. ARGBSetRow = ARGBSetRow_NEON;
  1330. }
  1331. }
  1332. #endif
  1333. #if defined(HAS_ARGBSETROW_X86)
  1334. if (TestCpuFlag(kCpuHasX86)) {
  1335. ARGBSetRow = ARGBSetRow_X86;
  1336. }
  1337. #endif
  1338. // Set plane
  1339. for (y = 0; y < height; ++y) {
  1340. ARGBSetRow(dst_argb, value, width);
  1341. dst_argb += dst_stride_argb;
  1342. }
  1343. return 0;
  1344. }
  1345. // Convert unattentuated ARGB to preattenuated ARGB.
  1346. // An unattenutated ARGB alpha blend uses the formula
  1347. // p = a * f + (1 - a) * b
  1348. // where
  1349. // p is output pixel
  1350. // f is foreground pixel
  1351. // b is background pixel
  1352. // a is alpha value from foreground pixel
  1353. // An preattenutated ARGB alpha blend uses the formula
  1354. // p = f + (1 - a) * b
  1355. // where
  1356. // f is foreground pixel premultiplied by alpha
  1357. LIBYUV_API
  1358. int ARGBAttenuate(const uint8* src_argb, int src_stride_argb,
  1359. uint8* dst_argb, int dst_stride_argb,
  1360. int width, int height) {
  1361. int y;
  1362. void (*ARGBAttenuateRow)(const uint8* src_argb, uint8* dst_argb,
  1363. int width) = ARGBAttenuateRow_C;
  1364. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  1365. return -1;
  1366. }
  1367. if (height < 0) {
  1368. height = -height;
  1369. src_argb = src_argb + (height - 1) * src_stride_argb;
  1370. src_stride_argb = -src_stride_argb;
  1371. }
  1372. // Coalesce rows.
  1373. if (src_stride_argb == width * 4 &&
  1374. dst_stride_argb == width * 4) {
  1375. width *= height;
  1376. height = 1;
  1377. src_stride_argb = dst_stride_argb = 0;
  1378. }
  1379. #if defined(HAS_ARGBATTENUATEROW_SSSE3)
  1380. if (TestCpuFlag(kCpuHasSSSE3)) {
  1381. ARGBAttenuateRow = ARGBAttenuateRow_Any_SSSE3;
  1382. if (IS_ALIGNED(width, 4)) {
  1383. ARGBAttenuateRow = ARGBAttenuateRow_SSSE3;
  1384. }
  1385. }
  1386. #endif
  1387. #if defined(HAS_ARGBATTENUATEROW_AVX2)
  1388. if (TestCpuFlag(kCpuHasAVX2)) {
  1389. ARGBAttenuateRow = ARGBAttenuateRow_Any_AVX2;
  1390. if (IS_ALIGNED(width, 8)) {
  1391. ARGBAttenuateRow = ARGBAttenuateRow_AVX2;
  1392. }
  1393. }
  1394. #endif
  1395. #if defined(HAS_ARGBATTENUATEROW_NEON)
  1396. if (TestCpuFlag(kCpuHasNEON)) {
  1397. ARGBAttenuateRow = ARGBAttenuateRow_Any_NEON;
  1398. if (IS_ALIGNED(width, 8)) {
  1399. ARGBAttenuateRow = ARGBAttenuateRow_NEON;
  1400. }
  1401. }
  1402. #endif
  1403. for (y = 0; y < height; ++y) {
  1404. ARGBAttenuateRow(src_argb, dst_argb, width);
  1405. src_argb += src_stride_argb;
  1406. dst_argb += dst_stride_argb;
  1407. }
  1408. return 0;
  1409. }
  1410. // Convert preattentuated ARGB to unattenuated ARGB.
  1411. LIBYUV_API
  1412. int ARGBUnattenuate(const uint8* src_argb, int src_stride_argb,
  1413. uint8* dst_argb, int dst_stride_argb,
  1414. int width, int height) {
  1415. int y;
  1416. void (*ARGBUnattenuateRow)(const uint8* src_argb, uint8* dst_argb,
  1417. int width) = ARGBUnattenuateRow_C;
  1418. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  1419. return -1;
  1420. }
  1421. if (height < 0) {
  1422. height = -height;
  1423. src_argb = src_argb + (height - 1) * src_stride_argb;
  1424. src_stride_argb = -src_stride_argb;
  1425. }
  1426. // Coalesce rows.
  1427. if (src_stride_argb == width * 4 &&
  1428. dst_stride_argb == width * 4) {
  1429. width *= height;
  1430. height = 1;
  1431. src_stride_argb = dst_stride_argb = 0;
  1432. }
  1433. #if defined(HAS_ARGBUNATTENUATEROW_SSE2)
  1434. if (TestCpuFlag(kCpuHasSSE2)) {
  1435. ARGBUnattenuateRow = ARGBUnattenuateRow_Any_SSE2;
  1436. if (IS_ALIGNED(width, 4)) {
  1437. ARGBUnattenuateRow = ARGBUnattenuateRow_SSE2;
  1438. }
  1439. }
  1440. #endif
  1441. #if defined(HAS_ARGBUNATTENUATEROW_AVX2)
  1442. if (TestCpuFlag(kCpuHasAVX2)) {
  1443. ARGBUnattenuateRow = ARGBUnattenuateRow_Any_AVX2;
  1444. if (IS_ALIGNED(width, 8)) {
  1445. ARGBUnattenuateRow = ARGBUnattenuateRow_AVX2;
  1446. }
  1447. }
  1448. #endif
  1449. // TODO(fbarchard): Neon version.
  1450. for (y = 0; y < height; ++y) {
  1451. ARGBUnattenuateRow(src_argb, dst_argb, width);
  1452. src_argb += src_stride_argb;
  1453. dst_argb += dst_stride_argb;
  1454. }
  1455. return 0;
  1456. }
  1457. // Convert ARGB to Grayed ARGB.
  1458. LIBYUV_API
  1459. int ARGBGrayTo(const uint8* src_argb, int src_stride_argb,
  1460. uint8* dst_argb, int dst_stride_argb,
  1461. int width, int height) {
  1462. int y;
  1463. void (*ARGBGrayRow)(const uint8* src_argb, uint8* dst_argb,
  1464. int width) = ARGBGrayRow_C;
  1465. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  1466. return -1;
  1467. }
  1468. if (height < 0) {
  1469. height = -height;
  1470. src_argb = src_argb + (height - 1) * src_stride_argb;
  1471. src_stride_argb = -src_stride_argb;
  1472. }
  1473. // Coalesce rows.
  1474. if (src_stride_argb == width * 4 &&
  1475. dst_stride_argb == width * 4) {
  1476. width *= height;
  1477. height = 1;
  1478. src_stride_argb = dst_stride_argb = 0;
  1479. }
  1480. #if defined(HAS_ARGBGRAYROW_SSSE3)
  1481. if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
  1482. ARGBGrayRow = ARGBGrayRow_SSSE3;
  1483. }
  1484. #endif
  1485. #if defined(HAS_ARGBGRAYROW_NEON)
  1486. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
  1487. ARGBGrayRow = ARGBGrayRow_NEON;
  1488. }
  1489. #endif
  1490. for (y = 0; y < height; ++y) {
  1491. ARGBGrayRow(src_argb, dst_argb, width);
  1492. src_argb += src_stride_argb;
  1493. dst_argb += dst_stride_argb;
  1494. }
  1495. return 0;
  1496. }
  1497. // Make a rectangle of ARGB gray scale.
  1498. LIBYUV_API
  1499. int ARGBGray(uint8* dst_argb, int dst_stride_argb,
  1500. int dst_x, int dst_y,
  1501. int width, int height) {
  1502. int y;
  1503. void (*ARGBGrayRow)(const uint8* src_argb, uint8* dst_argb,
  1504. int width) = ARGBGrayRow_C;
  1505. uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
  1506. if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0) {
  1507. return -1;
  1508. }
  1509. // Coalesce rows.
  1510. if (dst_stride_argb == width * 4) {
  1511. width *= height;
  1512. height = 1;
  1513. dst_stride_argb = 0;
  1514. }
  1515. #if defined(HAS_ARGBGRAYROW_SSSE3)
  1516. if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
  1517. ARGBGrayRow = ARGBGrayRow_SSSE3;
  1518. }
  1519. #endif
  1520. #if defined(HAS_ARGBGRAYROW_NEON)
  1521. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
  1522. ARGBGrayRow = ARGBGrayRow_NEON;
  1523. }
  1524. #endif
  1525. for (y = 0; y < height; ++y) {
  1526. ARGBGrayRow(dst, dst, width);
  1527. dst += dst_stride_argb;
  1528. }
  1529. return 0;
  1530. }
  1531. // Make a rectangle of ARGB Sepia tone.
  1532. LIBYUV_API
  1533. int ARGBSepia(uint8* dst_argb, int dst_stride_argb,
  1534. int dst_x, int dst_y, int width, int height) {
  1535. int y;
  1536. void (*ARGBSepiaRow)(uint8* dst_argb, int width) = ARGBSepiaRow_C;
  1537. uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
  1538. if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0) {
  1539. return -1;
  1540. }
  1541. // Coalesce rows.
  1542. if (dst_stride_argb == width * 4) {
  1543. width *= height;
  1544. height = 1;
  1545. dst_stride_argb = 0;
  1546. }
  1547. #if defined(HAS_ARGBSEPIAROW_SSSE3)
  1548. if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
  1549. ARGBSepiaRow = ARGBSepiaRow_SSSE3;
  1550. }
  1551. #endif
  1552. #if defined(HAS_ARGBSEPIAROW_NEON)
  1553. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
  1554. ARGBSepiaRow = ARGBSepiaRow_NEON;
  1555. }
  1556. #endif
  1557. for (y = 0; y < height; ++y) {
  1558. ARGBSepiaRow(dst, width);
  1559. dst += dst_stride_argb;
  1560. }
  1561. return 0;
  1562. }
  1563. // Apply a 4x4 matrix to each ARGB pixel.
  1564. // Note: Normally for shading, but can be used to swizzle or invert.
  1565. LIBYUV_API
  1566. int ARGBColorMatrix(const uint8* src_argb, int src_stride_argb,
  1567. uint8* dst_argb, int dst_stride_argb,
  1568. const int8* matrix_argb,
  1569. int width, int height) {
  1570. int y;
  1571. void (*ARGBColorMatrixRow)(const uint8* src_argb, uint8* dst_argb,
  1572. const int8* matrix_argb, int width) = ARGBColorMatrixRow_C;
  1573. if (!src_argb || !dst_argb || !matrix_argb || width <= 0 || height == 0) {
  1574. return -1;
  1575. }
  1576. if (height < 0) {
  1577. height = -height;
  1578. src_argb = src_argb + (height - 1) * src_stride_argb;
  1579. src_stride_argb = -src_stride_argb;
  1580. }
  1581. // Coalesce rows.
  1582. if (src_stride_argb == width * 4 &&
  1583. dst_stride_argb == width * 4) {
  1584. width *= height;
  1585. height = 1;
  1586. src_stride_argb = dst_stride_argb = 0;
  1587. }
  1588. #if defined(HAS_ARGBCOLORMATRIXROW_SSSE3)
  1589. if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 8)) {
  1590. ARGBColorMatrixRow = ARGBColorMatrixRow_SSSE3;
  1591. }
  1592. #endif
  1593. #if defined(HAS_ARGBCOLORMATRIXROW_NEON)
  1594. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
  1595. ARGBColorMatrixRow = ARGBColorMatrixRow_NEON;
  1596. }
  1597. #endif
  1598. for (y = 0; y < height; ++y) {
  1599. ARGBColorMatrixRow(src_argb, dst_argb, matrix_argb, width);
  1600. src_argb += src_stride_argb;
  1601. dst_argb += dst_stride_argb;
  1602. }
  1603. return 0;
  1604. }
  1605. // Apply a 4x3 matrix to each ARGB pixel.
  1606. // Deprecated.
  1607. LIBYUV_API
  1608. int RGBColorMatrix(uint8* dst_argb, int dst_stride_argb,
  1609. const int8* matrix_rgb,
  1610. int dst_x, int dst_y, int width, int height) {
  1611. SIMD_ALIGNED(int8 matrix_argb[16]);
  1612. uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
  1613. if (!dst_argb || !matrix_rgb || width <= 0 || height <= 0 ||
  1614. dst_x < 0 || dst_y < 0) {
  1615. return -1;
  1616. }
  1617. // Convert 4x3 7 bit matrix to 4x4 6 bit matrix.
  1618. matrix_argb[0] = matrix_rgb[0] / 2;
  1619. matrix_argb[1] = matrix_rgb[1] / 2;
  1620. matrix_argb[2] = matrix_rgb[2] / 2;
  1621. matrix_argb[3] = matrix_rgb[3] / 2;
  1622. matrix_argb[4] = matrix_rgb[4] / 2;
  1623. matrix_argb[5] = matrix_rgb[5] / 2;
  1624. matrix_argb[6] = matrix_rgb[6] / 2;
  1625. matrix_argb[7] = matrix_rgb[7] / 2;
  1626. matrix_argb[8] = matrix_rgb[8] / 2;
  1627. matrix_argb[9] = matrix_rgb[9] / 2;
  1628. matrix_argb[10] = matrix_rgb[10] / 2;
  1629. matrix_argb[11] = matrix_rgb[11] / 2;
  1630. matrix_argb[14] = matrix_argb[13] = matrix_argb[12] = 0;
  1631. matrix_argb[15] = 64; // 1.0
  1632. return ARGBColorMatrix((const uint8*)(dst), dst_stride_argb,
  1633. dst, dst_stride_argb,
  1634. &matrix_argb[0], width, height);
  1635. }
  1636. // Apply a color table each ARGB pixel.
  1637. // Table contains 256 ARGB values.
  1638. LIBYUV_API
  1639. int ARGBColorTable(uint8* dst_argb, int dst_stride_argb,
  1640. const uint8* table_argb,
  1641. int dst_x, int dst_y, int width, int height) {
  1642. int y;
  1643. void (*ARGBColorTableRow)(uint8* dst_argb, const uint8* table_argb,
  1644. int width) = ARGBColorTableRow_C;
  1645. uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
  1646. if (!dst_argb || !table_argb || width <= 0 || height <= 0 ||
  1647. dst_x < 0 || dst_y < 0) {
  1648. return -1;
  1649. }
  1650. // Coalesce rows.
  1651. if (dst_stride_argb == width * 4) {
  1652. width *= height;
  1653. height = 1;
  1654. dst_stride_argb = 0;
  1655. }
  1656. #if defined(HAS_ARGBCOLORTABLEROW_X86)
  1657. if (TestCpuFlag(kCpuHasX86)) {
  1658. ARGBColorTableRow = ARGBColorTableRow_X86;
  1659. }
  1660. #endif
  1661. for (y = 0; y < height; ++y) {
  1662. ARGBColorTableRow(dst, table_argb, width);
  1663. dst += dst_stride_argb;
  1664. }
  1665. return 0;
  1666. }
  1667. // Apply a color table each ARGB pixel but preserve destination alpha.
  1668. // Table contains 256 ARGB values.
  1669. LIBYUV_API
  1670. int RGBColorTable(uint8* dst_argb, int dst_stride_argb,
  1671. const uint8* table_argb,
  1672. int dst_x, int dst_y, int width, int height) {
  1673. int y;
  1674. void (*RGBColorTableRow)(uint8* dst_argb, const uint8* table_argb,
  1675. int width) = RGBColorTableRow_C;
  1676. uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
  1677. if (!dst_argb || !table_argb || width <= 0 || height <= 0 ||
  1678. dst_x < 0 || dst_y < 0) {
  1679. return -1;
  1680. }
  1681. // Coalesce rows.
  1682. if (dst_stride_argb == width * 4) {
  1683. width *= height;
  1684. height = 1;
  1685. dst_stride_argb = 0;
  1686. }
  1687. #if defined(HAS_RGBCOLORTABLEROW_X86)
  1688. if (TestCpuFlag(kCpuHasX86)) {
  1689. RGBColorTableRow = RGBColorTableRow_X86;
  1690. }
  1691. #endif
  1692. for (y = 0; y < height; ++y) {
  1693. RGBColorTableRow(dst, table_argb, width);
  1694. dst += dst_stride_argb;
  1695. }
  1696. return 0;
  1697. }
  1698. // ARGBQuantize is used to posterize art.
  1699. // e.g. rgb / qvalue * qvalue + qvalue / 2
  1700. // But the low levels implement efficiently with 3 parameters, and could be
  1701. // used for other high level operations.
  1702. // dst_argb[0] = (b * scale >> 16) * interval_size + interval_offset;
  1703. // where scale is 1 / interval_size as a fixed point value.
  1704. // The divide is replaces with a multiply by reciprocal fixed point multiply.
  1705. // Caveat - although SSE2 saturates, the C function does not and should be used
  1706. // with care if doing anything but quantization.
  1707. LIBYUV_API
  1708. int ARGBQuantize(uint8* dst_argb, int dst_stride_argb,
  1709. int scale, int interval_size, int interval_offset,
  1710. int dst_x, int dst_y, int width, int height) {
  1711. int y;
  1712. void (*ARGBQuantizeRow)(uint8* dst_argb, int scale, int interval_size,
  1713. int interval_offset, int width) = ARGBQuantizeRow_C;
  1714. uint8* dst = dst_argb + dst_y * dst_stride_argb + dst_x * 4;
  1715. if (!dst_argb || width <= 0 || height <= 0 || dst_x < 0 || dst_y < 0 ||
  1716. interval_size < 1 || interval_size > 255) {
  1717. return -1;
  1718. }
  1719. // Coalesce rows.
  1720. if (dst_stride_argb == width * 4) {
  1721. width *= height;
  1722. height = 1;
  1723. dst_stride_argb = 0;
  1724. }
  1725. #if defined(HAS_ARGBQUANTIZEROW_SSE2)
  1726. if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
  1727. ARGBQuantizeRow = ARGBQuantizeRow_SSE2;
  1728. }
  1729. #endif
  1730. #if defined(HAS_ARGBQUANTIZEROW_NEON)
  1731. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
  1732. ARGBQuantizeRow = ARGBQuantizeRow_NEON;
  1733. }
  1734. #endif
  1735. for (y = 0; y < height; ++y) {
  1736. ARGBQuantizeRow(dst, scale, interval_size, interval_offset, width);
  1737. dst += dst_stride_argb;
  1738. }
  1739. return 0;
  1740. }
  1741. // Computes table of cumulative sum for image where the value is the sum
  1742. // of all values above and to the left of the entry. Used by ARGBBlur.
  1743. LIBYUV_API
  1744. int ARGBComputeCumulativeSum(const uint8* src_argb, int src_stride_argb,
  1745. int32* dst_cumsum, int dst_stride32_cumsum,
  1746. int width, int height) {
  1747. int y;
  1748. void (*ComputeCumulativeSumRow)(const uint8* row, int32* cumsum,
  1749. const int32* previous_cumsum, int width) = ComputeCumulativeSumRow_C;
  1750. int32* previous_cumsum = dst_cumsum;
  1751. if (!dst_cumsum || !src_argb || width <= 0 || height <= 0) {
  1752. return -1;
  1753. }
  1754. #if defined(HAS_CUMULATIVESUMTOAVERAGEROW_SSE2)
  1755. if (TestCpuFlag(kCpuHasSSE2)) {
  1756. ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
  1757. }
  1758. #endif
  1759. memset(dst_cumsum, 0, width * sizeof(dst_cumsum[0]) * 4); // 4 int per pixel.
  1760. for (y = 0; y < height; ++y) {
  1761. ComputeCumulativeSumRow(src_argb, dst_cumsum, previous_cumsum, width);
  1762. previous_cumsum = dst_cumsum;
  1763. dst_cumsum += dst_stride32_cumsum;
  1764. src_argb += src_stride_argb;
  1765. }
  1766. return 0;
  1767. }
  1768. // Blur ARGB image.
  1769. // Caller should allocate CumulativeSum table of width * height * 16 bytes
  1770. // aligned to 16 byte boundary. height can be radius * 2 + 2 to save memory
  1771. // as the buffer is treated as circular.
  1772. LIBYUV_API
  1773. int ARGBBlur(const uint8* src_argb, int src_stride_argb,
  1774. uint8* dst_argb, int dst_stride_argb,
  1775. int32* dst_cumsum, int dst_stride32_cumsum,
  1776. int width, int height, int radius) {
  1777. int y;
  1778. void (*ComputeCumulativeSumRow)(const uint8 *row, int32 *cumsum,
  1779. const int32* previous_cumsum, int width) = ComputeCumulativeSumRow_C;
  1780. void (*CumulativeSumToAverageRow)(const int32* topleft, const int32* botleft,
  1781. int width, int area, uint8* dst, int count) = CumulativeSumToAverageRow_C;
  1782. int32* cumsum_bot_row;
  1783. int32* max_cumsum_bot_row;
  1784. int32* cumsum_top_row;
  1785. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  1786. return -1;
  1787. }
  1788. if (height < 0) {
  1789. height = -height;
  1790. src_argb = src_argb + (height - 1) * src_stride_argb;
  1791. src_stride_argb = -src_stride_argb;
  1792. }
  1793. if (radius > height) {
  1794. radius = height;
  1795. }
  1796. if (radius > (width / 2 - 1)) {
  1797. radius = width / 2 - 1;
  1798. }
  1799. if (radius <= 0) {
  1800. return -1;
  1801. }
  1802. #if defined(HAS_CUMULATIVESUMTOAVERAGEROW_SSE2)
  1803. if (TestCpuFlag(kCpuHasSSE2)) {
  1804. ComputeCumulativeSumRow = ComputeCumulativeSumRow_SSE2;
  1805. CumulativeSumToAverageRow = CumulativeSumToAverageRow_SSE2;
  1806. }
  1807. #endif
  1808. // Compute enough CumulativeSum for first row to be blurred. After this
  1809. // one row of CumulativeSum is updated at a time.
  1810. ARGBComputeCumulativeSum(src_argb, src_stride_argb,
  1811. dst_cumsum, dst_stride32_cumsum,
  1812. width, radius);
  1813. src_argb = src_argb + radius * src_stride_argb;
  1814. cumsum_bot_row = &dst_cumsum[(radius - 1) * dst_stride32_cumsum];
  1815. max_cumsum_bot_row = &dst_cumsum[(radius * 2 + 2) * dst_stride32_cumsum];
  1816. cumsum_top_row = &dst_cumsum[0];
  1817. for (y = 0; y < height; ++y) {
  1818. int top_y = ((y - radius - 1) >= 0) ? (y - radius - 1) : 0;
  1819. int bot_y = ((y + radius) < height) ? (y + radius) : (height - 1);
  1820. int area = radius * (bot_y - top_y);
  1821. int boxwidth = radius * 4;
  1822. int x;
  1823. int n;
  1824. // Increment cumsum_top_row pointer with circular buffer wrap around.
  1825. if (top_y) {
  1826. cumsum_top_row += dst_stride32_cumsum;
  1827. if (cumsum_top_row >= max_cumsum_bot_row) {
  1828. cumsum_top_row = dst_cumsum;
  1829. }
  1830. }
  1831. // Increment cumsum_bot_row pointer with circular buffer wrap around and
  1832. // then fill in a row of CumulativeSum.
  1833. if ((y + radius) < height) {
  1834. const int32* prev_cumsum_bot_row = cumsum_bot_row;
  1835. cumsum_bot_row += dst_stride32_cumsum;
  1836. if (cumsum_bot_row >= max_cumsum_bot_row) {
  1837. cumsum_bot_row = dst_cumsum;
  1838. }
  1839. ComputeCumulativeSumRow(src_argb, cumsum_bot_row, prev_cumsum_bot_row,
  1840. width);
  1841. src_argb += src_stride_argb;
  1842. }
  1843. // Left clipped.
  1844. for (x = 0; x < radius + 1; ++x) {
  1845. CumulativeSumToAverageRow(cumsum_top_row, cumsum_bot_row,
  1846. boxwidth, area, &dst_argb[x * 4], 1);
  1847. area += (bot_y - top_y);
  1848. boxwidth += 4;
  1849. }
  1850. // Middle unclipped.
  1851. n = (width - 1) - radius - x + 1;
  1852. CumulativeSumToAverageRow(cumsum_top_row, cumsum_bot_row,
  1853. boxwidth, area, &dst_argb[x * 4], n);
  1854. // Right clipped.
  1855. for (x += n; x <= width - 1; ++x) {
  1856. area -= (bot_y - top_y);
  1857. boxwidth -= 4;
  1858. CumulativeSumToAverageRow(cumsum_top_row + (x - radius - 1) * 4,
  1859. cumsum_bot_row + (x - radius - 1) * 4,
  1860. boxwidth, area, &dst_argb[x * 4], 1);
  1861. }
  1862. dst_argb += dst_stride_argb;
  1863. }
  1864. return 0;
  1865. }
  1866. // Multiply ARGB image by a specified ARGB value.
  1867. LIBYUV_API
  1868. int ARGBShade(const uint8* src_argb, int src_stride_argb,
  1869. uint8* dst_argb, int dst_stride_argb,
  1870. int width, int height, uint32 value) {
  1871. int y;
  1872. void (*ARGBShadeRow)(const uint8* src_argb, uint8* dst_argb,
  1873. int width, uint32 value) = ARGBShadeRow_C;
  1874. if (!src_argb || !dst_argb || width <= 0 || height == 0 || value == 0u) {
  1875. return -1;
  1876. }
  1877. if (height < 0) {
  1878. height = -height;
  1879. src_argb = src_argb + (height - 1) * src_stride_argb;
  1880. src_stride_argb = -src_stride_argb;
  1881. }
  1882. // Coalesce rows.
  1883. if (src_stride_argb == width * 4 &&
  1884. dst_stride_argb == width * 4) {
  1885. width *= height;
  1886. height = 1;
  1887. src_stride_argb = dst_stride_argb = 0;
  1888. }
  1889. #if defined(HAS_ARGBSHADEROW_SSE2)
  1890. if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 4)) {
  1891. ARGBShadeRow = ARGBShadeRow_SSE2;
  1892. }
  1893. #endif
  1894. #if defined(HAS_ARGBSHADEROW_NEON)
  1895. if (TestCpuFlag(kCpuHasNEON) && IS_ALIGNED(width, 8)) {
  1896. ARGBShadeRow = ARGBShadeRow_NEON;
  1897. }
  1898. #endif
  1899. for (y = 0; y < height; ++y) {
  1900. ARGBShadeRow(src_argb, dst_argb, width, value);
  1901. src_argb += src_stride_argb;
  1902. dst_argb += dst_stride_argb;
  1903. }
  1904. return 0;
  1905. }
  1906. // Interpolate 2 planes by specified amount (0 to 255).
  1907. LIBYUV_API
  1908. int InterpolatePlane(const uint8* src0, int src_stride0,
  1909. const uint8* src1, int src_stride1,
  1910. uint8* dst, int dst_stride,
  1911. int width, int height, int interpolation) {
  1912. int y;
  1913. void (*InterpolateRow)(uint8* dst_ptr, const uint8* src_ptr,
  1914. ptrdiff_t src_stride, int dst_width,
  1915. int source_y_fraction) = InterpolateRow_C;
  1916. if (!src0 || !src1 || !dst || width <= 0 || height == 0) {
  1917. return -1;
  1918. }
  1919. // Negative height means invert the image.
  1920. if (height < 0) {
  1921. height = -height;
  1922. dst = dst + (height - 1) * dst_stride;
  1923. dst_stride = -dst_stride;
  1924. }
  1925. // Coalesce rows.
  1926. if (src_stride0 == width &&
  1927. src_stride1 == width &&
  1928. dst_stride == width) {
  1929. width *= height;
  1930. height = 1;
  1931. src_stride0 = src_stride1 = dst_stride = 0;
  1932. }
  1933. #if defined(HAS_INTERPOLATEROW_SSSE3)
  1934. if (TestCpuFlag(kCpuHasSSSE3)) {
  1935. InterpolateRow = InterpolateRow_Any_SSSE3;
  1936. if (IS_ALIGNED(width, 16)) {
  1937. InterpolateRow = InterpolateRow_SSSE3;
  1938. }
  1939. }
  1940. #endif
  1941. #if defined(HAS_INTERPOLATEROW_AVX2)
  1942. if (TestCpuFlag(kCpuHasAVX2)) {
  1943. InterpolateRow = InterpolateRow_Any_AVX2;
  1944. if (IS_ALIGNED(width, 32)) {
  1945. InterpolateRow = InterpolateRow_AVX2;
  1946. }
  1947. }
  1948. #endif
  1949. #if defined(HAS_INTERPOLATEROW_NEON)
  1950. if (TestCpuFlag(kCpuHasNEON)) {
  1951. InterpolateRow = InterpolateRow_Any_NEON;
  1952. if (IS_ALIGNED(width, 16)) {
  1953. InterpolateRow = InterpolateRow_NEON;
  1954. }
  1955. }
  1956. #endif
  1957. #if defined(HAS_INTERPOLATEROW_DSPR2)
  1958. if (TestCpuFlag(kCpuHasDSPR2) &&
  1959. IS_ALIGNED(src0, 4) && IS_ALIGNED(src_stride0, 4) &&
  1960. IS_ALIGNED(src1, 4) && IS_ALIGNED(src_stride1, 4) &&
  1961. IS_ALIGNED(dst, 4) && IS_ALIGNED(dst_stride, 4) &&
  1962. IS_ALIGNED(width, 4)) {
  1963. InterpolateRow = InterpolateRow_DSPR2;
  1964. }
  1965. #endif
  1966. for (y = 0; y < height; ++y) {
  1967. InterpolateRow(dst, src0, src1 - src0, width, interpolation);
  1968. src0 += src_stride0;
  1969. src1 += src_stride1;
  1970. dst += dst_stride;
  1971. }
  1972. return 0;
  1973. }
  1974. // Interpolate 2 ARGB images by specified amount (0 to 255).
  1975. LIBYUV_API
  1976. int ARGBInterpolate(const uint8* src_argb0, int src_stride_argb0,
  1977. const uint8* src_argb1, int src_stride_argb1,
  1978. uint8* dst_argb, int dst_stride_argb,
  1979. int width, int height, int interpolation) {
  1980. return InterpolatePlane(src_argb0, src_stride_argb0,
  1981. src_argb1, src_stride_argb1,
  1982. dst_argb, dst_stride_argb,
  1983. width * 4, height, interpolation);
  1984. }
  1985. // Interpolate 2 YUV images by specified amount (0 to 255).
  1986. LIBYUV_API
  1987. int I420Interpolate(const uint8* src0_y, int src0_stride_y,
  1988. const uint8* src0_u, int src0_stride_u,
  1989. const uint8* src0_v, int src0_stride_v,
  1990. const uint8* src1_y, int src1_stride_y,
  1991. const uint8* src1_u, int src1_stride_u,
  1992. const uint8* src1_v, int src1_stride_v,
  1993. uint8* dst_y, int dst_stride_y,
  1994. uint8* dst_u, int dst_stride_u,
  1995. uint8* dst_v, int dst_stride_v,
  1996. int width, int height, int interpolation) {
  1997. int halfwidth = (width + 1) >> 1;
  1998. int halfheight = (height + 1) >> 1;
  1999. if (!src0_y || !src0_u || !src0_v ||
  2000. !src1_y || !src1_u || !src1_v ||
  2001. !dst_y || !dst_u || !dst_v ||
  2002. width <= 0 || height == 0) {
  2003. return -1;
  2004. }
  2005. InterpolatePlane(src0_y, src0_stride_y,
  2006. src1_y, src1_stride_y,
  2007. dst_y, dst_stride_y,
  2008. width, height, interpolation);
  2009. InterpolatePlane(src0_u, src0_stride_u,
  2010. src1_u, src1_stride_u,
  2011. dst_u, dst_stride_u,
  2012. halfwidth, halfheight, interpolation);
  2013. InterpolatePlane(src0_v, src0_stride_v,
  2014. src1_v, src1_stride_v,
  2015. dst_v, dst_stride_v,
  2016. halfwidth, halfheight, interpolation);
  2017. return 0;
  2018. }
  2019. // Shuffle ARGB channel order. e.g. BGRA to ARGB.
  2020. LIBYUV_API
  2021. int ARGBShuffle(const uint8* src_bgra, int src_stride_bgra,
  2022. uint8* dst_argb, int dst_stride_argb,
  2023. const uint8* shuffler, int width, int height) {
  2024. int y;
  2025. void (*ARGBShuffleRow)(const uint8* src_bgra, uint8* dst_argb,
  2026. const uint8* shuffler, int width) = ARGBShuffleRow_C;
  2027. if (!src_bgra || !dst_argb ||
  2028. width <= 0 || height == 0) {
  2029. return -1;
  2030. }
  2031. // Negative height means invert the image.
  2032. if (height < 0) {
  2033. height = -height;
  2034. src_bgra = src_bgra + (height - 1) * src_stride_bgra;
  2035. src_stride_bgra = -src_stride_bgra;
  2036. }
  2037. // Coalesce rows.
  2038. if (src_stride_bgra == width * 4 &&
  2039. dst_stride_argb == width * 4) {
  2040. width *= height;
  2041. height = 1;
  2042. src_stride_bgra = dst_stride_argb = 0;
  2043. }
  2044. #if defined(HAS_ARGBSHUFFLEROW_SSE2)
  2045. if (TestCpuFlag(kCpuHasSSE2)) {
  2046. ARGBShuffleRow = ARGBShuffleRow_Any_SSE2;
  2047. if (IS_ALIGNED(width, 4)) {
  2048. ARGBShuffleRow = ARGBShuffleRow_SSE2;
  2049. }
  2050. }
  2051. #endif
  2052. #if defined(HAS_ARGBSHUFFLEROW_SSSE3)
  2053. if (TestCpuFlag(kCpuHasSSSE3)) {
  2054. ARGBShuffleRow = ARGBShuffleRow_Any_SSSE3;
  2055. if (IS_ALIGNED(width, 8)) {
  2056. ARGBShuffleRow = ARGBShuffleRow_SSSE3;
  2057. }
  2058. }
  2059. #endif
  2060. #if defined(HAS_ARGBSHUFFLEROW_AVX2)
  2061. if (TestCpuFlag(kCpuHasAVX2)) {
  2062. ARGBShuffleRow = ARGBShuffleRow_Any_AVX2;
  2063. if (IS_ALIGNED(width, 16)) {
  2064. ARGBShuffleRow = ARGBShuffleRow_AVX2;
  2065. }
  2066. }
  2067. #endif
  2068. #if defined(HAS_ARGBSHUFFLEROW_NEON)
  2069. if (TestCpuFlag(kCpuHasNEON)) {
  2070. ARGBShuffleRow = ARGBShuffleRow_Any_NEON;
  2071. if (IS_ALIGNED(width, 4)) {
  2072. ARGBShuffleRow = ARGBShuffleRow_NEON;
  2073. }
  2074. }
  2075. #endif
  2076. for (y = 0; y < height; ++y) {
  2077. ARGBShuffleRow(src_bgra, dst_argb, shuffler, width);
  2078. src_bgra += src_stride_bgra;
  2079. dst_argb += dst_stride_argb;
  2080. }
  2081. return 0;
  2082. }
  2083. // Sobel ARGB effect.
  2084. static int ARGBSobelize(const uint8* src_argb, int src_stride_argb,
  2085. uint8* dst_argb, int dst_stride_argb,
  2086. int width, int height,
  2087. void (*SobelRow)(const uint8* src_sobelx,
  2088. const uint8* src_sobely,
  2089. uint8* dst, int width)) {
  2090. int y;
  2091. void (*ARGBToYJRow)(const uint8* src_argb, uint8* dst_g, int width) =
  2092. ARGBToYJRow_C;
  2093. void (*SobelYRow)(const uint8* src_y0, const uint8* src_y1,
  2094. uint8* dst_sobely, int width) = SobelYRow_C;
  2095. void (*SobelXRow)(const uint8* src_y0, const uint8* src_y1,
  2096. const uint8* src_y2, uint8* dst_sobely, int width) =
  2097. SobelXRow_C;
  2098. const int kEdge = 16; // Extra pixels at start of row for extrude/align.
  2099. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  2100. return -1;
  2101. }
  2102. // Negative height means invert the image.
  2103. if (height < 0) {
  2104. height = -height;
  2105. src_argb = src_argb + (height - 1) * src_stride_argb;
  2106. src_stride_argb = -src_stride_argb;
  2107. }
  2108. #if defined(HAS_ARGBTOYJROW_SSSE3)
  2109. if (TestCpuFlag(kCpuHasSSSE3)) {
  2110. ARGBToYJRow = ARGBToYJRow_Any_SSSE3;
  2111. if (IS_ALIGNED(width, 16)) {
  2112. ARGBToYJRow = ARGBToYJRow_SSSE3;
  2113. }
  2114. }
  2115. #endif
  2116. #if defined(HAS_ARGBTOYJROW_AVX2)
  2117. if (TestCpuFlag(kCpuHasAVX2)) {
  2118. ARGBToYJRow = ARGBToYJRow_Any_AVX2;
  2119. if (IS_ALIGNED(width, 32)) {
  2120. ARGBToYJRow = ARGBToYJRow_AVX2;
  2121. }
  2122. }
  2123. #endif
  2124. #if defined(HAS_ARGBTOYJROW_NEON)
  2125. if (TestCpuFlag(kCpuHasNEON)) {
  2126. ARGBToYJRow = ARGBToYJRow_Any_NEON;
  2127. if (IS_ALIGNED(width, 8)) {
  2128. ARGBToYJRow = ARGBToYJRow_NEON;
  2129. }
  2130. }
  2131. #endif
  2132. #if defined(HAS_SOBELYROW_SSE2)
  2133. if (TestCpuFlag(kCpuHasSSE2)) {
  2134. SobelYRow = SobelYRow_SSE2;
  2135. }
  2136. #endif
  2137. #if defined(HAS_SOBELYROW_NEON)
  2138. if (TestCpuFlag(kCpuHasNEON)) {
  2139. SobelYRow = SobelYRow_NEON;
  2140. }
  2141. #endif
  2142. #if defined(HAS_SOBELXROW_SSE2)
  2143. if (TestCpuFlag(kCpuHasSSE2)) {
  2144. SobelXRow = SobelXRow_SSE2;
  2145. }
  2146. #endif
  2147. #if defined(HAS_SOBELXROW_NEON)
  2148. if (TestCpuFlag(kCpuHasNEON)) {
  2149. SobelXRow = SobelXRow_NEON;
  2150. }
  2151. #endif
  2152. {
  2153. // 3 rows with edges before/after.
  2154. const int kRowSize = (width + kEdge + 31) & ~31;
  2155. align_buffer_64(rows, kRowSize * 2 + (kEdge + kRowSize * 3 + kEdge));
  2156. uint8* row_sobelx = rows;
  2157. uint8* row_sobely = rows + kRowSize;
  2158. uint8* row_y = rows + kRowSize * 2;
  2159. // Convert first row.
  2160. uint8* row_y0 = row_y + kEdge;
  2161. uint8* row_y1 = row_y0 + kRowSize;
  2162. uint8* row_y2 = row_y1 + kRowSize;
  2163. ARGBToYJRow(src_argb, row_y0, width);
  2164. row_y0[-1] = row_y0[0];
  2165. memset(row_y0 + width, row_y0[width - 1], 16); // Extrude 16 for valgrind.
  2166. ARGBToYJRow(src_argb, row_y1, width);
  2167. row_y1[-1] = row_y1[0];
  2168. memset(row_y1 + width, row_y1[width - 1], 16);
  2169. memset(row_y2 + width, 0, 16);
  2170. for (y = 0; y < height; ++y) {
  2171. // Convert next row of ARGB to G.
  2172. if (y < (height - 1)) {
  2173. src_argb += src_stride_argb;
  2174. }
  2175. ARGBToYJRow(src_argb, row_y2, width);
  2176. row_y2[-1] = row_y2[0];
  2177. row_y2[width] = row_y2[width - 1];
  2178. SobelXRow(row_y0 - 1, row_y1 - 1, row_y2 - 1, row_sobelx, width);
  2179. SobelYRow(row_y0 - 1, row_y2 - 1, row_sobely, width);
  2180. SobelRow(row_sobelx, row_sobely, dst_argb, width);
  2181. // Cycle thru circular queue of 3 row_y buffers.
  2182. {
  2183. uint8* row_yt = row_y0;
  2184. row_y0 = row_y1;
  2185. row_y1 = row_y2;
  2186. row_y2 = row_yt;
  2187. }
  2188. dst_argb += dst_stride_argb;
  2189. }
  2190. free_aligned_buffer_64(rows);
  2191. }
  2192. return 0;
  2193. }
  2194. // Sobel ARGB effect.
  2195. LIBYUV_API
  2196. int ARGBSobel(const uint8* src_argb, int src_stride_argb,
  2197. uint8* dst_argb, int dst_stride_argb,
  2198. int width, int height) {
  2199. void (*SobelRow)(const uint8* src_sobelx, const uint8* src_sobely,
  2200. uint8* dst_argb, int width) = SobelRow_C;
  2201. #if defined(HAS_SOBELROW_SSE2)
  2202. if (TestCpuFlag(kCpuHasSSE2)) {
  2203. SobelRow = SobelRow_Any_SSE2;
  2204. if (IS_ALIGNED(width, 16)) {
  2205. SobelRow = SobelRow_SSE2;
  2206. }
  2207. }
  2208. #endif
  2209. #if defined(HAS_SOBELROW_NEON)
  2210. if (TestCpuFlag(kCpuHasNEON)) {
  2211. SobelRow = SobelRow_Any_NEON;
  2212. if (IS_ALIGNED(width, 8)) {
  2213. SobelRow = SobelRow_NEON;
  2214. }
  2215. }
  2216. #endif
  2217. return ARGBSobelize(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
  2218. width, height, SobelRow);
  2219. }
  2220. // Sobel ARGB effect with planar output.
  2221. LIBYUV_API
  2222. int ARGBSobelToPlane(const uint8* src_argb, int src_stride_argb,
  2223. uint8* dst_y, int dst_stride_y,
  2224. int width, int height) {
  2225. void (*SobelToPlaneRow)(const uint8* src_sobelx, const uint8* src_sobely,
  2226. uint8* dst_, int width) = SobelToPlaneRow_C;
  2227. #if defined(HAS_SOBELTOPLANEROW_SSE2)
  2228. if (TestCpuFlag(kCpuHasSSE2)) {
  2229. SobelToPlaneRow = SobelToPlaneRow_Any_SSE2;
  2230. if (IS_ALIGNED(width, 16)) {
  2231. SobelToPlaneRow = SobelToPlaneRow_SSE2;
  2232. }
  2233. }
  2234. #endif
  2235. #if defined(HAS_SOBELTOPLANEROW_NEON)
  2236. if (TestCpuFlag(kCpuHasNEON)) {
  2237. SobelToPlaneRow = SobelToPlaneRow_Any_NEON;
  2238. if (IS_ALIGNED(width, 16)) {
  2239. SobelToPlaneRow = SobelToPlaneRow_NEON;
  2240. }
  2241. }
  2242. #endif
  2243. return ARGBSobelize(src_argb, src_stride_argb, dst_y, dst_stride_y,
  2244. width, height, SobelToPlaneRow);
  2245. }
  2246. // SobelXY ARGB effect.
  2247. // Similar to Sobel, but also stores Sobel X in R and Sobel Y in B. G = Sobel.
  2248. LIBYUV_API
  2249. int ARGBSobelXY(const uint8* src_argb, int src_stride_argb,
  2250. uint8* dst_argb, int dst_stride_argb,
  2251. int width, int height) {
  2252. void (*SobelXYRow)(const uint8* src_sobelx, const uint8* src_sobely,
  2253. uint8* dst_argb, int width) = SobelXYRow_C;
  2254. #if defined(HAS_SOBELXYROW_SSE2)
  2255. if (TestCpuFlag(kCpuHasSSE2)) {
  2256. SobelXYRow = SobelXYRow_Any_SSE2;
  2257. if (IS_ALIGNED(width, 16)) {
  2258. SobelXYRow = SobelXYRow_SSE2;
  2259. }
  2260. }
  2261. #endif
  2262. #if defined(HAS_SOBELXYROW_NEON)
  2263. if (TestCpuFlag(kCpuHasNEON)) {
  2264. SobelXYRow = SobelXYRow_Any_NEON;
  2265. if (IS_ALIGNED(width, 8)) {
  2266. SobelXYRow = SobelXYRow_NEON;
  2267. }
  2268. }
  2269. #endif
  2270. return ARGBSobelize(src_argb, src_stride_argb, dst_argb, dst_stride_argb,
  2271. width, height, SobelXYRow);
  2272. }
  2273. // Apply a 4x4 polynomial to each ARGB pixel.
  2274. LIBYUV_API
  2275. int ARGBPolynomial(const uint8* src_argb, int src_stride_argb,
  2276. uint8* dst_argb, int dst_stride_argb,
  2277. const float* poly,
  2278. int width, int height) {
  2279. int y;
  2280. void (*ARGBPolynomialRow)(const uint8* src_argb,
  2281. uint8* dst_argb, const float* poly,
  2282. int width) = ARGBPolynomialRow_C;
  2283. if (!src_argb || !dst_argb || !poly || width <= 0 || height == 0) {
  2284. return -1;
  2285. }
  2286. // Negative height means invert the image.
  2287. if (height < 0) {
  2288. height = -height;
  2289. src_argb = src_argb + (height - 1) * src_stride_argb;
  2290. src_stride_argb = -src_stride_argb;
  2291. }
  2292. // Coalesce rows.
  2293. if (src_stride_argb == width * 4 &&
  2294. dst_stride_argb == width * 4) {
  2295. width *= height;
  2296. height = 1;
  2297. src_stride_argb = dst_stride_argb = 0;
  2298. }
  2299. #if defined(HAS_ARGBPOLYNOMIALROW_SSE2)
  2300. if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(width, 2)) {
  2301. ARGBPolynomialRow = ARGBPolynomialRow_SSE2;
  2302. }
  2303. #endif
  2304. #if defined(HAS_ARGBPOLYNOMIALROW_AVX2)
  2305. if (TestCpuFlag(kCpuHasAVX2) && TestCpuFlag(kCpuHasFMA3) &&
  2306. IS_ALIGNED(width, 2)) {
  2307. ARGBPolynomialRow = ARGBPolynomialRow_AVX2;
  2308. }
  2309. #endif
  2310. for (y = 0; y < height; ++y) {
  2311. ARGBPolynomialRow(src_argb, dst_argb, poly, width);
  2312. src_argb += src_stride_argb;
  2313. dst_argb += dst_stride_argb;
  2314. }
  2315. return 0;
  2316. }
  2317. // Apply a lumacolortable to each ARGB pixel.
  2318. LIBYUV_API
  2319. int ARGBLumaColorTable(const uint8* src_argb, int src_stride_argb,
  2320. uint8* dst_argb, int dst_stride_argb,
  2321. const uint8* luma,
  2322. int width, int height) {
  2323. int y;
  2324. void (*ARGBLumaColorTableRow)(const uint8* src_argb, uint8* dst_argb,
  2325. int width, const uint8* luma, const uint32 lumacoeff) =
  2326. ARGBLumaColorTableRow_C;
  2327. if (!src_argb || !dst_argb || !luma || width <= 0 || height == 0) {
  2328. return -1;
  2329. }
  2330. // Negative height means invert the image.
  2331. if (height < 0) {
  2332. height = -height;
  2333. src_argb = src_argb + (height - 1) * src_stride_argb;
  2334. src_stride_argb = -src_stride_argb;
  2335. }
  2336. // Coalesce rows.
  2337. if (src_stride_argb == width * 4 &&
  2338. dst_stride_argb == width * 4) {
  2339. width *= height;
  2340. height = 1;
  2341. src_stride_argb = dst_stride_argb = 0;
  2342. }
  2343. #if defined(HAS_ARGBLUMACOLORTABLEROW_SSSE3)
  2344. if (TestCpuFlag(kCpuHasSSSE3) && IS_ALIGNED(width, 4)) {
  2345. ARGBLumaColorTableRow = ARGBLumaColorTableRow_SSSE3;
  2346. }
  2347. #endif
  2348. for (y = 0; y < height; ++y) {
  2349. ARGBLumaColorTableRow(src_argb, dst_argb, width, luma, 0x00264b0f);
  2350. src_argb += src_stride_argb;
  2351. dst_argb += dst_stride_argb;
  2352. }
  2353. return 0;
  2354. }
  2355. // Copy Alpha from one ARGB image to another.
  2356. LIBYUV_API
  2357. int ARGBCopyAlpha(const uint8* src_argb, int src_stride_argb,
  2358. uint8* dst_argb, int dst_stride_argb,
  2359. int width, int height) {
  2360. int y;
  2361. void (*ARGBCopyAlphaRow)(const uint8* src_argb, uint8* dst_argb, int width) =
  2362. ARGBCopyAlphaRow_C;
  2363. if (!src_argb || !dst_argb || width <= 0 || height == 0) {
  2364. return -1;
  2365. }
  2366. // Negative height means invert the image.
  2367. if (height < 0) {
  2368. height = -height;
  2369. src_argb = src_argb + (height - 1) * src_stride_argb;
  2370. src_stride_argb = -src_stride_argb;
  2371. }
  2372. // Coalesce rows.
  2373. if (src_stride_argb == width * 4 &&
  2374. dst_stride_argb == width * 4) {
  2375. width *= height;
  2376. height = 1;
  2377. src_stride_argb = dst_stride_argb = 0;
  2378. }
  2379. #if defined(HAS_ARGBCOPYALPHAROW_SSE2)
  2380. if (TestCpuFlag(kCpuHasSSE2)) {
  2381. ARGBCopyAlphaRow = ARGBCopyAlphaRow_Any_SSE2;
  2382. if (IS_ALIGNED(width, 8)) {
  2383. ARGBCopyAlphaRow = ARGBCopyAlphaRow_SSE2;
  2384. }
  2385. }
  2386. #endif
  2387. #if defined(HAS_ARGBCOPYALPHAROW_AVX2)
  2388. if (TestCpuFlag(kCpuHasAVX2)) {
  2389. ARGBCopyAlphaRow = ARGBCopyAlphaRow_Any_AVX2;
  2390. if (IS_ALIGNED(width, 16)) {
  2391. ARGBCopyAlphaRow = ARGBCopyAlphaRow_AVX2;
  2392. }
  2393. }
  2394. #endif
  2395. for (y = 0; y < height; ++y) {
  2396. ARGBCopyAlphaRow(src_argb, dst_argb, width);
  2397. src_argb += src_stride_argb;
  2398. dst_argb += dst_stride_argb;
  2399. }
  2400. return 0;
  2401. }
  2402. // Extract just the alpha channel from ARGB.
  2403. LIBYUV_API
  2404. int ARGBExtractAlpha(const uint8* src_argb, int src_stride,
  2405. uint8* dst_a, int dst_stride,
  2406. int width, int height) {
  2407. if (!src_argb || !dst_a || width <= 0 || height == 0) {
  2408. return -1;
  2409. }
  2410. // Negative height means invert the image.
  2411. if (height < 0) {
  2412. height = -height;
  2413. src_argb += (height - 1) * src_stride;
  2414. src_stride = -src_stride;
  2415. }
  2416. // Coalesce rows.
  2417. if (src_stride == width * 4 && dst_stride == width) {
  2418. width *= height;
  2419. height = 1;
  2420. src_stride = dst_stride = 0;
  2421. }
  2422. void (*ARGBExtractAlphaRow)(const uint8 *src_argb, uint8 *dst_a, int width) =
  2423. ARGBExtractAlphaRow_C;
  2424. #if defined(HAS_ARGBEXTRACTALPHAROW_SSE2)
  2425. if (TestCpuFlag(kCpuHasSSE2)) {
  2426. ARGBExtractAlphaRow = IS_ALIGNED(width, 8) ? ARGBExtractAlphaRow_SSE2
  2427. : ARGBExtractAlphaRow_Any_SSE2;
  2428. }
  2429. #endif
  2430. #if defined(HAS_ARGBEXTRACTALPHAROW_NEON)
  2431. if (TestCpuFlag(kCpuHasNEON)) {
  2432. ARGBExtractAlphaRow = IS_ALIGNED(width, 16) ? ARGBExtractAlphaRow_NEON
  2433. : ARGBExtractAlphaRow_Any_NEON;
  2434. }
  2435. #endif
  2436. for (int y = 0; y < height; ++y) {
  2437. ARGBExtractAlphaRow(src_argb, dst_a, width);
  2438. src_argb += src_stride;
  2439. dst_a += dst_stride;
  2440. }
  2441. return 0;
  2442. }
  2443. // Copy a planar Y channel to the alpha channel of a destination ARGB image.
  2444. LIBYUV_API
  2445. int ARGBCopyYToAlpha(const uint8* src_y, int src_stride_y,
  2446. uint8* dst_argb, int dst_stride_argb,
  2447. int width, int height) {
  2448. int y;
  2449. void (*ARGBCopyYToAlphaRow)(const uint8* src_y, uint8* dst_argb, int width) =
  2450. ARGBCopyYToAlphaRow_C;
  2451. if (!src_y || !dst_argb || width <= 0 || height == 0) {
  2452. return -1;
  2453. }
  2454. // Negative height means invert the image.
  2455. if (height < 0) {
  2456. height = -height;
  2457. src_y = src_y + (height - 1) * src_stride_y;
  2458. src_stride_y = -src_stride_y;
  2459. }
  2460. // Coalesce rows.
  2461. if (src_stride_y == width &&
  2462. dst_stride_argb == width * 4) {
  2463. width *= height;
  2464. height = 1;
  2465. src_stride_y = dst_stride_argb = 0;
  2466. }
  2467. #if defined(HAS_ARGBCOPYYTOALPHAROW_SSE2)
  2468. if (TestCpuFlag(kCpuHasSSE2)) {
  2469. ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_Any_SSE2;
  2470. if (IS_ALIGNED(width, 8)) {
  2471. ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_SSE2;
  2472. }
  2473. }
  2474. #endif
  2475. #if defined(HAS_ARGBCOPYYTOALPHAROW_AVX2)
  2476. if (TestCpuFlag(kCpuHasAVX2)) {
  2477. ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_Any_AVX2;
  2478. if (IS_ALIGNED(width, 16)) {
  2479. ARGBCopyYToAlphaRow = ARGBCopyYToAlphaRow_AVX2;
  2480. }
  2481. }
  2482. #endif
  2483. for (y = 0; y < height; ++y) {
  2484. ARGBCopyYToAlphaRow(src_y, dst_argb, width);
  2485. src_y += src_stride_y;
  2486. dst_argb += dst_stride_argb;
  2487. }
  2488. return 0;
  2489. }
  2490. // TODO(fbarchard): Consider if width is even Y channel can be split
  2491. // directly. A SplitUVRow_Odd function could copy the remaining chroma.
  2492. LIBYUV_API
  2493. int YUY2ToNV12(const uint8* src_yuy2, int src_stride_yuy2,
  2494. uint8* dst_y, int dst_stride_y,
  2495. uint8* dst_uv, int dst_stride_uv,
  2496. int width, int height) {
  2497. int y;
  2498. int halfwidth = (width + 1) >> 1;
  2499. void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
  2500. int width) = SplitUVRow_C;
  2501. void (*InterpolateRow)(uint8* dst_ptr, const uint8* src_ptr,
  2502. ptrdiff_t src_stride, int dst_width,
  2503. int source_y_fraction) = InterpolateRow_C;
  2504. if (!src_yuy2 ||
  2505. !dst_y || !dst_uv ||
  2506. width <= 0 || height == 0) {
  2507. return -1;
  2508. }
  2509. // Negative height means invert the image.
  2510. if (height < 0) {
  2511. height = -height;
  2512. src_yuy2 = src_yuy2 + (height - 1) * src_stride_yuy2;
  2513. src_stride_yuy2 = -src_stride_yuy2;
  2514. }
  2515. #if defined(HAS_SPLITUVROW_SSE2)
  2516. if (TestCpuFlag(kCpuHasSSE2)) {
  2517. SplitUVRow = SplitUVRow_Any_SSE2;
  2518. if (IS_ALIGNED(width, 16)) {
  2519. SplitUVRow = SplitUVRow_SSE2;
  2520. }
  2521. }
  2522. #endif
  2523. #if defined(HAS_SPLITUVROW_AVX2)
  2524. if (TestCpuFlag(kCpuHasAVX2)) {
  2525. SplitUVRow = SplitUVRow_Any_AVX2;
  2526. if (IS_ALIGNED(width, 32)) {
  2527. SplitUVRow = SplitUVRow_AVX2;
  2528. }
  2529. }
  2530. #endif
  2531. #if defined(HAS_SPLITUVROW_NEON)
  2532. if (TestCpuFlag(kCpuHasNEON)) {
  2533. SplitUVRow = SplitUVRow_Any_NEON;
  2534. if (IS_ALIGNED(width, 16)) {
  2535. SplitUVRow = SplitUVRow_NEON;
  2536. }
  2537. }
  2538. #endif
  2539. #if defined(HAS_INTERPOLATEROW_SSSE3)
  2540. if (TestCpuFlag(kCpuHasSSSE3)) {
  2541. InterpolateRow = InterpolateRow_Any_SSSE3;
  2542. if (IS_ALIGNED(width, 16)) {
  2543. InterpolateRow = InterpolateRow_SSSE3;
  2544. }
  2545. }
  2546. #endif
  2547. #if defined(HAS_INTERPOLATEROW_AVX2)
  2548. if (TestCpuFlag(kCpuHasAVX2)) {
  2549. InterpolateRow = InterpolateRow_Any_AVX2;
  2550. if (IS_ALIGNED(width, 32)) {
  2551. InterpolateRow = InterpolateRow_AVX2;
  2552. }
  2553. }
  2554. #endif
  2555. #if defined(HAS_INTERPOLATEROW_NEON)
  2556. if (TestCpuFlag(kCpuHasNEON)) {
  2557. InterpolateRow = InterpolateRow_Any_NEON;
  2558. if (IS_ALIGNED(width, 16)) {
  2559. InterpolateRow = InterpolateRow_NEON;
  2560. }
  2561. }
  2562. #endif
  2563. {
  2564. int awidth = halfwidth * 2;
  2565. // row of y and 2 rows of uv
  2566. align_buffer_64(rows, awidth * 3);
  2567. for (y = 0; y < height - 1; y += 2) {
  2568. // Split Y from UV.
  2569. SplitUVRow(src_yuy2, rows, rows + awidth, awidth);
  2570. memcpy(dst_y, rows, width);
  2571. SplitUVRow(src_yuy2 + src_stride_yuy2, rows, rows + awidth * 2, awidth);
  2572. memcpy(dst_y + dst_stride_y, rows, width);
  2573. InterpolateRow(dst_uv, rows + awidth, awidth, awidth, 128);
  2574. src_yuy2 += src_stride_yuy2 * 2;
  2575. dst_y += dst_stride_y * 2;
  2576. dst_uv += dst_stride_uv;
  2577. }
  2578. if (height & 1) {
  2579. // Split Y from UV.
  2580. SplitUVRow(src_yuy2, rows, dst_uv, awidth);
  2581. memcpy(dst_y, rows, width);
  2582. }
  2583. free_aligned_buffer_64(rows);
  2584. }
  2585. return 0;
  2586. }
  2587. LIBYUV_API
  2588. int UYVYToNV12(const uint8* src_uyvy, int src_stride_uyvy,
  2589. uint8* dst_y, int dst_stride_y,
  2590. uint8* dst_uv, int dst_stride_uv,
  2591. int width, int height) {
  2592. int y;
  2593. int halfwidth = (width + 1) >> 1;
  2594. void (*SplitUVRow)(const uint8* src_uv, uint8* dst_u, uint8* dst_v,
  2595. int width) = SplitUVRow_C;
  2596. void (*InterpolateRow)(uint8* dst_ptr, const uint8* src_ptr,
  2597. ptrdiff_t src_stride, int dst_width,
  2598. int source_y_fraction) = InterpolateRow_C;
  2599. if (!src_uyvy ||
  2600. !dst_y || !dst_uv ||
  2601. width <= 0 || height == 0) {
  2602. return -1;
  2603. }
  2604. // Negative height means invert the image.
  2605. if (height < 0) {
  2606. height = -height;
  2607. src_uyvy = src_uyvy + (height - 1) * src_stride_uyvy;
  2608. src_stride_uyvy = -src_stride_uyvy;
  2609. }
  2610. #if defined(HAS_SPLITUVROW_SSE2)
  2611. if (TestCpuFlag(kCpuHasSSE2)) {
  2612. SplitUVRow = SplitUVRow_Any_SSE2;
  2613. if (IS_ALIGNED(width, 16)) {
  2614. SplitUVRow = SplitUVRow_SSE2;
  2615. }
  2616. }
  2617. #endif
  2618. #if defined(HAS_SPLITUVROW_AVX2)
  2619. if (TestCpuFlag(kCpuHasAVX2)) {
  2620. SplitUVRow = SplitUVRow_Any_AVX2;
  2621. if (IS_ALIGNED(width, 32)) {
  2622. SplitUVRow = SplitUVRow_AVX2;
  2623. }
  2624. }
  2625. #endif
  2626. #if defined(HAS_SPLITUVROW_NEON)
  2627. if (TestCpuFlag(kCpuHasNEON)) {
  2628. SplitUVRow = SplitUVRow_Any_NEON;
  2629. if (IS_ALIGNED(width, 16)) {
  2630. SplitUVRow = SplitUVRow_NEON;
  2631. }
  2632. }
  2633. #endif
  2634. #if defined(HAS_INTERPOLATEROW_SSSE3)
  2635. if (TestCpuFlag(kCpuHasSSSE3)) {
  2636. InterpolateRow = InterpolateRow_Any_SSSE3;
  2637. if (IS_ALIGNED(width, 16)) {
  2638. InterpolateRow = InterpolateRow_SSSE3;
  2639. }
  2640. }
  2641. #endif
  2642. #if defined(HAS_INTERPOLATEROW_AVX2)
  2643. if (TestCpuFlag(kCpuHasAVX2)) {
  2644. InterpolateRow = InterpolateRow_Any_AVX2;
  2645. if (IS_ALIGNED(width, 32)) {
  2646. InterpolateRow = InterpolateRow_AVX2;
  2647. }
  2648. }
  2649. #endif
  2650. #if defined(HAS_INTERPOLATEROW_NEON)
  2651. if (TestCpuFlag(kCpuHasNEON)) {
  2652. InterpolateRow = InterpolateRow_Any_NEON;
  2653. if (IS_ALIGNED(width, 16)) {
  2654. InterpolateRow = InterpolateRow_NEON;
  2655. }
  2656. }
  2657. #endif
  2658. {
  2659. int awidth = halfwidth * 2;
  2660. // row of y and 2 rows of uv
  2661. align_buffer_64(rows, awidth * 3);
  2662. for (y = 0; y < height - 1; y += 2) {
  2663. // Split Y from UV.
  2664. SplitUVRow(src_uyvy, rows + awidth, rows, awidth);
  2665. memcpy(dst_y, rows, width);
  2666. SplitUVRow(src_uyvy + src_stride_uyvy, rows + awidth * 2, rows, awidth);
  2667. memcpy(dst_y + dst_stride_y, rows, width);
  2668. InterpolateRow(dst_uv, rows + awidth, awidth, awidth, 128);
  2669. src_uyvy += src_stride_uyvy * 2;
  2670. dst_y += dst_stride_y * 2;
  2671. dst_uv += dst_stride_uv;
  2672. }
  2673. if (height & 1) {
  2674. // Split Y from UV.
  2675. SplitUVRow(src_uyvy, dst_uv, rows, awidth);
  2676. memcpy(dst_y, rows, width);
  2677. }
  2678. free_aligned_buffer_64(rows);
  2679. }
  2680. return 0;
  2681. }
  2682. #ifdef __cplusplus
  2683. } // extern "C"
  2684. } // namespace libyuv
  2685. #endif