You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mat_pixel_resize.cpp 34 kB

7 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058
  1. // Copyright 2018 Tencent
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. #include "mat.h"
  4. #include <limits.h>
  5. #if __ARM_NEON
  6. #include <arm_neon.h>
  7. #endif // __ARM_NEON
  8. #include "platform.h"
  9. namespace ncnn {
  10. #if NCNN_PIXEL
  11. static void vresize_two(const short* rows0p, const short* rows1p, int wsize, unsigned char* Dp0, unsigned char* Dp1, short b0, short b1, short b2, short b3)
  12. {
  13. int dx = 0;
  14. #if __ARM_NEON
  15. int16x8_t _b0 = vdupq_n_s16(b0);
  16. int16x8_t _b1 = vdupq_n_s16(b1);
  17. int16x8_t _b2 = vdupq_n_s16(b2);
  18. int16x8_t _b3 = vdupq_n_s16(b3);
  19. for (; dx + 15 < wsize; dx += 16)
  20. {
  21. int16x8_t _r00 = vld1q_s16(rows0p);
  22. int16x8_t _r01 = vld1q_s16(rows0p + 8);
  23. int16x8_t _r10 = vld1q_s16(rows1p);
  24. int16x8_t _r11 = vld1q_s16(rows1p + 8);
  25. int16x8_t _acc00 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r00, _b0), 1), vqdmulhq_s16(_r10, _b1), 1);
  26. int16x8_t _acc01 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r01, _b0), 1), vqdmulhq_s16(_r11, _b1), 1);
  27. int16x8_t _acc10 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r00, _b2), 1), vqdmulhq_s16(_r10, _b3), 1);
  28. int16x8_t _acc11 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r01, _b2), 1), vqdmulhq_s16(_r11, _b3), 1);
  29. uint8x16_t _Dp0 = vcombine_u8(vqrshrun_n_s16(_acc00, 2), vqrshrun_n_s16(_acc01, 2));
  30. uint8x16_t _Dp1 = vcombine_u8(vqrshrun_n_s16(_acc10, 2), vqrshrun_n_s16(_acc11, 2));
  31. vst1q_u8(Dp0, _Dp0);
  32. vst1q_u8(Dp1, _Dp1);
  33. Dp0 += 16;
  34. Dp1 += 16;
  35. rows0p += 16;
  36. rows1p += 16;
  37. }
  38. for (; dx + 7 < wsize; dx += 8)
  39. {
  40. int16x8_t _r0 = vld1q_s16(rows0p);
  41. int16x8_t _r1 = vld1q_s16(rows1p);
  42. int16x8_t _acc0 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r0, _b0), 1), vqdmulhq_s16(_r1, _b1), 1);
  43. int16x8_t _acc1 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r0, _b2), 1), vqdmulhq_s16(_r1, _b3), 1);
  44. uint8x8_t _Dp0 = vqrshrun_n_s16(_acc0, 2);
  45. uint8x8_t _Dp1 = vqrshrun_n_s16(_acc1, 2);
  46. vst1_u8(Dp0, _Dp0);
  47. vst1_u8(Dp1, _Dp1);
  48. Dp0 += 8;
  49. Dp1 += 8;
  50. rows0p += 8;
  51. rows1p += 8;
  52. }
  53. #endif // __ARM_NEON
  54. #if __SSE2__
  55. __m128i _b0 = _mm_set1_epi16(b0);
  56. __m128i _b1 = _mm_set1_epi16(b1);
  57. __m128i _b2 = _mm_set1_epi16(b2);
  58. __m128i _b3 = _mm_set1_epi16(b3);
  59. __m128i _v2 = _mm_set1_epi16(2);
  60. for (; dx + 15 < wsize; dx += 16)
  61. {
  62. __m128i _r00 = _mm_loadu_si128((const __m128i*)rows0p);
  63. __m128i _r01 = _mm_loadu_si128((const __m128i*)(rows0p + 8));
  64. __m128i _r10 = _mm_loadu_si128((const __m128i*)rows1p);
  65. __m128i _r11 = _mm_loadu_si128((const __m128i*)(rows1p + 8));
  66. __m128i _acc00 = _mm_add_epi16(_mm_mulhi_epi16(_r00, _b0), _mm_mulhi_epi16(_r10, _b1));
  67. __m128i _acc01 = _mm_add_epi16(_mm_mulhi_epi16(_r01, _b0), _mm_mulhi_epi16(_r11, _b1));
  68. __m128i _acc10 = _mm_add_epi16(_mm_mulhi_epi16(_r00, _b2), _mm_mulhi_epi16(_r10, _b3));
  69. __m128i _acc11 = _mm_add_epi16(_mm_mulhi_epi16(_r01, _b2), _mm_mulhi_epi16(_r11, _b3));
  70. _acc00 = _mm_srai_epi16(_mm_add_epi16(_acc00, _v2), 2);
  71. _acc01 = _mm_srai_epi16(_mm_add_epi16(_acc01, _v2), 2);
  72. _acc10 = _mm_srai_epi16(_mm_add_epi16(_acc10, _v2), 2);
  73. _acc11 = _mm_srai_epi16(_mm_add_epi16(_acc11, _v2), 2);
  74. __m128i _Dp0 = _mm_packus_epi16(_acc00, _acc01);
  75. __m128i _Dp1 = _mm_packus_epi16(_acc10, _acc11);
  76. _mm_storeu_si128((__m128i*)Dp0, _Dp0);
  77. _mm_storeu_si128((__m128i*)Dp1, _Dp1);
  78. Dp0 += 16;
  79. Dp1 += 16;
  80. rows0p += 16;
  81. rows1p += 16;
  82. }
  83. for (; dx + 7 < wsize; dx += 8)
  84. {
  85. __m128i _r0 = _mm_loadu_si128((const __m128i*)rows0p);
  86. __m128i _r1 = _mm_loadu_si128((const __m128i*)rows1p);
  87. __m128i _acc0 = _mm_add_epi16(_mm_mulhi_epi16(_r0, _b0), _mm_mulhi_epi16(_r1, _b1));
  88. __m128i _acc1 = _mm_add_epi16(_mm_mulhi_epi16(_r0, _b2), _mm_mulhi_epi16(_r1, _b3));
  89. _acc0 = _mm_srai_epi16(_mm_add_epi16(_acc0, _v2), 2);
  90. _acc1 = _mm_srai_epi16(_mm_add_epi16(_acc1, _v2), 2);
  91. __m128i _Dp0 = _mm_packus_epi16(_acc0, _acc0);
  92. __m128i _Dp1 = _mm_packus_epi16(_acc1, _acc1);
  93. _mm_storel_epi64((__m128i*)Dp0, _Dp0);
  94. _mm_storel_epi64((__m128i*)Dp1, _Dp1);
  95. Dp0 += 8;
  96. Dp1 += 8;
  97. rows0p += 8;
  98. rows1p += 8;
  99. }
  100. #endif // __SSE2__
  101. for (; dx < wsize; dx++)
  102. {
  103. short s0 = *rows0p++;
  104. short s1 = *rows1p++;
  105. *Dp0++ = (unsigned char)(((short)((b0 * s0) >> 16) + (short)((b1 * s1) >> 16) + 2) >> 2);
  106. *Dp1++ = (unsigned char)(((short)((b2 * s0) >> 16) + (short)((b3 * s1) >> 16) + 2) >> 2);
  107. }
  108. }
  109. static void vresize_one(const short* rows0p, const short* rows1p, int wsize, unsigned char* Dp, short b0, short b1)
  110. {
  111. int dx = 0;
  112. #if __ARM_NEON
  113. int16x8_t _b0 = vdupq_n_s16(b0);
  114. int16x8_t _b1 = vdupq_n_s16(b1);
  115. for (; dx + 15 < wsize; dx += 16)
  116. {
  117. int16x8_t _r00 = vld1q_s16(rows0p);
  118. int16x8_t _r01 = vld1q_s16(rows0p + 8);
  119. int16x8_t _r10 = vld1q_s16(rows1p);
  120. int16x8_t _r11 = vld1q_s16(rows1p + 8);
  121. int16x8_t _acc0 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r00, _b0), 1), vqdmulhq_s16(_r10, _b1), 1);
  122. int16x8_t _acc1 = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r01, _b0), 1), vqdmulhq_s16(_r11, _b1), 1);
  123. uint8x16_t _Dp = vcombine_u8(vqrshrun_n_s16(_acc0, 2), vqrshrun_n_s16(_acc1, 2));
  124. vst1q_u8(Dp, _Dp);
  125. Dp += 16;
  126. rows0p += 16;
  127. rows1p += 16;
  128. }
  129. for (; dx + 7 < wsize; dx += 8)
  130. {
  131. int16x8_t _r0 = vld1q_s16(rows0p);
  132. int16x8_t _r1 = vld1q_s16(rows1p);
  133. int16x8_t _acc = vsraq_n_s16(vshrq_n_s16(vqdmulhq_s16(_r0, _b0), 1), vqdmulhq_s16(_r1, _b1), 1);
  134. uint8x8_t _Dp = vqrshrun_n_s16(_acc, 2);
  135. vst1_u8(Dp, _Dp);
  136. Dp += 8;
  137. rows0p += 8;
  138. rows1p += 8;
  139. }
  140. #endif // __ARM_NEON
  141. #if __SSE2__
  142. __m128i _b0 = _mm_set1_epi16(b0);
  143. __m128i _b1 = _mm_set1_epi16(b1);
  144. __m128i _v2 = _mm_set1_epi16(2);
  145. for (; dx + 15 < wsize; dx += 16)
  146. {
  147. __m128i _r00 = _mm_loadu_si128((const __m128i*)rows0p);
  148. __m128i _r01 = _mm_loadu_si128((const __m128i*)(rows0p + 8));
  149. __m128i _r10 = _mm_loadu_si128((const __m128i*)rows1p);
  150. __m128i _r11 = _mm_loadu_si128((const __m128i*)(rows1p + 8));
  151. __m128i _acc0 = _mm_add_epi16(_mm_mulhi_epi16(_r00, _b0), _mm_mulhi_epi16(_r10, _b1));
  152. __m128i _acc1 = _mm_add_epi16(_mm_mulhi_epi16(_r01, _b0), _mm_mulhi_epi16(_r11, _b1));
  153. _acc0 = _mm_srai_epi16(_mm_add_epi16(_acc0, _v2), 2);
  154. _acc1 = _mm_srai_epi16(_mm_add_epi16(_acc1, _v2), 2);
  155. __m128i _Dp = _mm_packus_epi16(_acc0, _acc1);
  156. _mm_storeu_si128((__m128i*)Dp, _Dp);
  157. Dp += 16;
  158. rows0p += 16;
  159. rows1p += 16;
  160. }
  161. for (; dx + 7 < wsize; dx += 8)
  162. {
  163. __m128i _r0 = _mm_loadu_si128((const __m128i*)rows0p);
  164. __m128i _r1 = _mm_loadu_si128((const __m128i*)rows1p);
  165. __m128i _acc = _mm_add_epi16(_mm_mulhi_epi16(_r0, _b0), _mm_mulhi_epi16(_r1, _b1));
  166. _acc = _mm_srai_epi16(_mm_add_epi16(_acc, _v2), 2);
  167. __m128i _Dp = _mm_packus_epi16(_acc, _acc);
  168. _mm_storel_epi64((__m128i*)Dp, _Dp);
  169. Dp += 8;
  170. rows0p += 8;
  171. rows1p += 8;
  172. }
  173. #endif // __SSE2__
  174. for (; dx < wsize; dx++)
  175. {
  176. short s0 = *rows0p++;
  177. short s1 = *rows1p++;
  178. *Dp++ = (unsigned char)(((short)((b0 * s0) >> 16) + (short)((b1 * s1) >> 16) + 2) >> 2);
  179. }
  180. }
  181. void resize_bilinear_c1(const unsigned char* src, int srcw, int srch, unsigned char* dst, int w, int h)
  182. {
  183. return resize_bilinear_c1(src, srcw, srch, srcw, dst, w, h, w);
  184. }
  185. void resize_bilinear_c2(const unsigned char* src, int srcw, int srch, unsigned char* dst, int w, int h)
  186. {
  187. return resize_bilinear_c2(src, srcw, srch, srcw * 2, dst, w, h, w * 2);
  188. }
  189. void resize_bilinear_c3(const unsigned char* src, int srcw, int srch, unsigned char* dst, int w, int h)
  190. {
  191. return resize_bilinear_c3(src, srcw, srch, srcw * 3, dst, w, h, w * 3);
  192. }
  193. void resize_bilinear_c4(const unsigned char* src, int srcw, int srch, unsigned char* dst, int w, int h)
  194. {
  195. return resize_bilinear_c4(src, srcw, srch, srcw * 4, dst, w, h, w * 4);
  196. }
  197. void resize_bilinear_c1(const unsigned char* src, int srcw, int srch, int srcstride, unsigned char* dst, int w, int h, int stride)
  198. {
  199. const int INTER_RESIZE_COEF_BITS = 11;
  200. const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
  201. double scale_x = (double)srcw / w;
  202. double scale_y = (double)srch / h;
  203. int* buf = new int[w + h + w + h];
  204. int* xofs = buf; //new int[w];
  205. int* yofs = buf + w; //new int[h];
  206. short* ialpha = (short*)(buf + w + h); //new short[w * 2];
  207. short* ibeta = (short*)(buf + w + h + w); //new short[h * 2];
  208. float fx;
  209. float fy;
  210. int sx;
  211. int sy;
  212. #define SATURATE_CAST_SHORT(X) (short)::std::min(::std::max((int)(X + (X >= 0.f ? 0.5f : -0.5f)), SHRT_MIN), SHRT_MAX);
  213. for (int dx = 0; dx < w; dx++)
  214. {
  215. fx = (float)((dx + 0.5) * scale_x - 0.5);
  216. sx = static_cast<int>(floor(fx));
  217. fx -= sx;
  218. if (sx < 0)
  219. {
  220. sx = 0;
  221. fx = 0.f;
  222. }
  223. if (sx >= srcw - 1)
  224. {
  225. sx = srcw - 2;
  226. fx = 1.f;
  227. }
  228. xofs[dx] = sx;
  229. float a0 = (1.f - fx) * INTER_RESIZE_COEF_SCALE;
  230. float a1 = fx * INTER_RESIZE_COEF_SCALE;
  231. ialpha[dx * 2] = SATURATE_CAST_SHORT(a0);
  232. ialpha[dx * 2 + 1] = SATURATE_CAST_SHORT(a1);
  233. }
  234. for (int dy = 0; dy < h; dy++)
  235. {
  236. fy = (float)((dy + 0.5) * scale_y - 0.5);
  237. sy = static_cast<int>(floor(fy));
  238. fy -= sy;
  239. if (sy < 0)
  240. {
  241. sy = 0;
  242. fy = 0.f;
  243. }
  244. if (sy >= srch - 1)
  245. {
  246. sy = srch - 2;
  247. fy = 1.f;
  248. }
  249. yofs[dy] = sy;
  250. float b0 = (1.f - fy) * INTER_RESIZE_COEF_SCALE;
  251. float b1 = fy * INTER_RESIZE_COEF_SCALE;
  252. ibeta[dy * 2] = SATURATE_CAST_SHORT(b0);
  253. ibeta[dy * 2 + 1] = SATURATE_CAST_SHORT(b1);
  254. }
  255. #undef SATURATE_CAST_SHORT
  256. // loop body
  257. Mat rowsbuf0(w, (size_t)2u);
  258. Mat rowsbuf1(w, (size_t)2u);
  259. short* rows0 = (short*)rowsbuf0.data;
  260. short* rows1 = (short*)rowsbuf1.data;
  261. int prev_sy1 = -2;
  262. for (int dy = 0; dy < h; dy++)
  263. {
  264. sy = yofs[dy];
  265. if (sy == prev_sy1)
  266. {
  267. // reuse all rows
  268. }
  269. else if (sy == prev_sy1 + 1)
  270. {
  271. // hresize one row
  272. short* rows0_old = rows0;
  273. rows0 = rows1;
  274. rows1 = rows0_old;
  275. const unsigned char* S1 = src + srcstride * (sy + 1);
  276. const short* ialphap = ialpha;
  277. short* rows1p = rows1;
  278. for (int dx = 0; dx < w; dx++)
  279. {
  280. sx = xofs[dx];
  281. short a0 = ialphap[0];
  282. short a1 = ialphap[1];
  283. const unsigned char* S1p = S1 + sx;
  284. rows1p[dx] = (S1p[0] * a0 + S1p[1] * a1) >> 4;
  285. ialphap += 2;
  286. }
  287. }
  288. else
  289. {
  290. // hresize two rows
  291. const unsigned char* S0 = src + srcstride * (sy);
  292. const unsigned char* S1 = src + srcstride * (sy + 1);
  293. const short* ialphap = ialpha;
  294. short* rows0p = rows0;
  295. short* rows1p = rows1;
  296. for (int dx = 0; dx < w; dx++)
  297. {
  298. sx = xofs[dx];
  299. short a0 = ialphap[0];
  300. short a1 = ialphap[1];
  301. const unsigned char* S0p = S0 + sx;
  302. const unsigned char* S1p = S1 + sx;
  303. rows0p[dx] = (S0p[0] * a0 + S0p[1] * a1) >> 4;
  304. rows1p[dx] = (S1p[0] * a0 + S1p[1] * a1) >> 4;
  305. ialphap += 2;
  306. }
  307. }
  308. prev_sy1 = sy;
  309. if (dy + 1 < h && yofs[dy + 1] == sy)
  310. {
  311. // vresize for two rows
  312. unsigned char* Dp0 = dst + stride * dy;
  313. unsigned char* Dp1 = dst + stride * (dy + 1);
  314. vresize_two(rows0, rows1, w, Dp0, Dp1, ibeta[0], ibeta[1], ibeta[2], ibeta[3]);
  315. ibeta += 4;
  316. dy += 1;
  317. }
  318. else
  319. {
  320. // vresize
  321. unsigned char* Dp = dst + stride * dy;
  322. vresize_one(rows0, rows1, w, Dp, ibeta[0], ibeta[1]);
  323. ibeta += 2;
  324. }
  325. }
  326. delete[] buf;
  327. }
  328. void resize_bilinear_c2(const unsigned char* src, int srcw, int srch, int srcstride, unsigned char* dst, int w, int h, int stride)
  329. {
  330. const int INTER_RESIZE_COEF_BITS = 11;
  331. const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
  332. double scale_x = (double)srcw / w;
  333. double scale_y = (double)srch / h;
  334. int* buf = new int[w + h + w + h];
  335. int* xofs = buf; //new int[w];
  336. int* yofs = buf + w; //new int[h];
  337. short* ialpha = (short*)(buf + w + h); //new short[w * 2];
  338. short* ibeta = (short*)(buf + w + h + w); //new short[h * 2];
  339. float fx;
  340. float fy;
  341. int sx;
  342. int sy;
  343. #define SATURATE_CAST_SHORT(X) (short)::std::min(::std::max((int)(X + (X >= 0.f ? 0.5f : -0.5f)), SHRT_MIN), SHRT_MAX);
  344. for (int dx = 0; dx < w; dx++)
  345. {
  346. fx = (float)((dx + 0.5) * scale_x - 0.5);
  347. sx = static_cast<int>(floor(fx));
  348. fx -= sx;
  349. if (sx < 0)
  350. {
  351. sx = 0;
  352. fx = 0.f;
  353. }
  354. if (sx >= srcw - 1)
  355. {
  356. sx = srcw - 2;
  357. fx = 1.f;
  358. }
  359. xofs[dx] = sx * 2;
  360. float a0 = (1.f - fx) * INTER_RESIZE_COEF_SCALE;
  361. float a1 = fx * INTER_RESIZE_COEF_SCALE;
  362. ialpha[dx * 2] = SATURATE_CAST_SHORT(a0);
  363. ialpha[dx * 2 + 1] = SATURATE_CAST_SHORT(a1);
  364. }
  365. for (int dy = 0; dy < h; dy++)
  366. {
  367. fy = (float)((dy + 0.5) * scale_y - 0.5);
  368. sy = static_cast<int>(floor(fy));
  369. fy -= sy;
  370. if (sy < 0)
  371. {
  372. sy = 0;
  373. fy = 0.f;
  374. }
  375. if (sy >= srch - 1)
  376. {
  377. sy = srch - 2;
  378. fy = 1.f;
  379. }
  380. yofs[dy] = sy;
  381. float b0 = (1.f - fy) * INTER_RESIZE_COEF_SCALE;
  382. float b1 = fy * INTER_RESIZE_COEF_SCALE;
  383. ibeta[dy * 2] = SATURATE_CAST_SHORT(b0);
  384. ibeta[dy * 2 + 1] = SATURATE_CAST_SHORT(b1);
  385. }
  386. #undef SATURATE_CAST_SHORT
  387. // loop body
  388. Mat rowsbuf0(w * 2 + 2, (size_t)2u);
  389. Mat rowsbuf1(w * 2 + 2, (size_t)2u);
  390. short* rows0 = (short*)rowsbuf0.data;
  391. short* rows1 = (short*)rowsbuf1.data;
  392. int prev_sy1 = -2;
  393. for (int dy = 0; dy < h; dy++)
  394. {
  395. sy = yofs[dy];
  396. if (sy == prev_sy1)
  397. {
  398. // reuse all rows
  399. }
  400. else if (sy == prev_sy1 + 1)
  401. {
  402. // hresize one row
  403. short* rows0_old = rows0;
  404. rows0 = rows1;
  405. rows1 = rows0_old;
  406. const unsigned char* S1 = src + srcstride * (sy + 1);
  407. const short* ialphap = ialpha;
  408. short* rows1p = rows1;
  409. for (int dx = 0; dx < w; dx++)
  410. {
  411. sx = xofs[dx];
  412. const unsigned char* S1p = S1 + sx;
  413. #if __ARM_NEON
  414. int16x4_t _a0a1XX = vld1_s16(ialphap);
  415. int16x4_t _a0a0a1a1 = vzip_s16(_a0a1XX, _a0a1XX).val[0];
  416. uint8x8_t _S1 = uint8x8_t();
  417. _S1 = vld1_lane_u8(S1p, _S1, 0);
  418. _S1 = vld1_lane_u8(S1p + 1, _S1, 1);
  419. _S1 = vld1_lane_u8(S1p + 2, _S1, 2);
  420. _S1 = vld1_lane_u8(S1p + 3, _S1, 3);
  421. int16x8_t _S116 = vreinterpretq_s16_u16(vmovl_u8(_S1));
  422. int16x4_t _S1lowhigh = vget_low_s16(_S116);
  423. int32x4_t _S1ma0a1 = vmull_s16(_S1lowhigh, _a0a0a1a1);
  424. int32x2_t _rows1low = vadd_s32(vget_low_s32(_S1ma0a1), vget_high_s32(_S1ma0a1));
  425. int32x4_t _rows1 = vcombine_s32(_rows1low, vget_high_s32(_S1ma0a1));
  426. int16x4_t _rows1_sr4 = vshrn_n_s32(_rows1, 4);
  427. vst1_s16(rows1p, _rows1_sr4);
  428. #else
  429. short a0 = ialphap[0];
  430. short a1 = ialphap[1];
  431. rows1p[0] = (S1p[0] * a0 + S1p[2] * a1) >> 4;
  432. rows1p[1] = (S1p[1] * a0 + S1p[3] * a1) >> 4;
  433. #endif // __ARM_NEON
  434. ialphap += 2;
  435. rows1p += 2;
  436. }
  437. }
  438. else
  439. {
  440. // hresize two rows
  441. const unsigned char* S0 = src + srcstride * (sy);
  442. const unsigned char* S1 = src + srcstride * (sy + 1);
  443. const short* ialphap = ialpha;
  444. short* rows0p = rows0;
  445. short* rows1p = rows1;
  446. for (int dx = 0; dx < w; dx++)
  447. {
  448. sx = xofs[dx];
  449. short a0 = ialphap[0];
  450. short a1 = ialphap[1];
  451. const unsigned char* S0p = S0 + sx;
  452. const unsigned char* S1p = S1 + sx;
  453. #if __ARM_NEON
  454. int16x4_t _a0 = vdup_n_s16(a0);
  455. int16x4_t _a1 = vdup_n_s16(a1);
  456. uint8x8_t _S0 = uint8x8_t();
  457. uint8x8_t _S1 = uint8x8_t();
  458. _S0 = vld1_lane_u8(S0p, _S0, 0);
  459. _S0 = vld1_lane_u8(S0p + 1, _S0, 1);
  460. _S0 = vld1_lane_u8(S0p + 2, _S0, 2);
  461. _S0 = vld1_lane_u8(S0p + 3, _S0, 3);
  462. _S1 = vld1_lane_u8(S1p, _S1, 0);
  463. _S1 = vld1_lane_u8(S1p + 1, _S1, 1);
  464. _S1 = vld1_lane_u8(S1p + 2, _S1, 2);
  465. _S1 = vld1_lane_u8(S1p + 3, _S1, 3);
  466. int16x8_t _S016 = vreinterpretq_s16_u16(vmovl_u8(_S0));
  467. int16x8_t _S116 = vreinterpretq_s16_u16(vmovl_u8(_S1));
  468. int16x4_t _S0lowhigh = vget_low_s16(_S016);
  469. int16x4_t _S1lowhigh = vget_low_s16(_S116);
  470. int32x2x2_t _S0S1low_S0S1high = vtrn_s32(vreinterpret_s32_s16(_S0lowhigh), vreinterpret_s32_s16(_S1lowhigh));
  471. int32x4_t _rows01 = vmull_s16(vreinterpret_s16_s32(_S0S1low_S0S1high.val[0]), _a0);
  472. _rows01 = vmlal_s16(_rows01, vreinterpret_s16_s32(_S0S1low_S0S1high.val[1]), _a1);
  473. int16x4_t _rows01_sr4 = vshrn_n_s32(_rows01, 4);
  474. int16x4_t _rows1_sr4 = vext_s16(_rows01_sr4, _rows01_sr4, 2);
  475. vst1_s16(rows0p, _rows01_sr4);
  476. vst1_s16(rows1p, _rows1_sr4);
  477. #else
  478. rows0p[0] = (S0p[0] * a0 + S0p[2] * a1) >> 4;
  479. rows0p[1] = (S0p[1] * a0 + S0p[3] * a1) >> 4;
  480. rows1p[0] = (S1p[0] * a0 + S1p[2] * a1) >> 4;
  481. rows1p[1] = (S1p[1] * a0 + S1p[3] * a1) >> 4;
  482. #endif // __ARM_NEON
  483. ialphap += 2;
  484. rows0p += 2;
  485. rows1p += 2;
  486. }
  487. }
  488. prev_sy1 = sy;
  489. if (dy + 1 < h && yofs[dy + 1] == sy)
  490. {
  491. // vresize for two rows
  492. unsigned char* Dp0 = dst + stride * dy;
  493. unsigned char* Dp1 = dst + stride * (dy + 1);
  494. vresize_two(rows0, rows1, w * 2, Dp0, Dp1, ibeta[0], ibeta[1], ibeta[2], ibeta[3]);
  495. ibeta += 4;
  496. dy += 1;
  497. }
  498. else
  499. {
  500. // vresize
  501. unsigned char* Dp = dst + stride * dy;
  502. vresize_one(rows0, rows1, w * 2, Dp, ibeta[0], ibeta[1]);
  503. ibeta += 2;
  504. }
  505. }
  506. delete[] buf;
  507. }
  508. void resize_bilinear_c3(const unsigned char* src, int srcw, int srch, int srcstride, unsigned char* dst, int w, int h, int stride)
  509. {
  510. const int INTER_RESIZE_COEF_BITS = 11;
  511. const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
  512. double scale_x = (double)srcw / w;
  513. double scale_y = (double)srch / h;
  514. int* buf = new int[w + h + w + h];
  515. int* xofs = buf; //new int[w];
  516. int* yofs = buf + w; //new int[h];
  517. short* ialpha = (short*)(buf + w + h); //new short[w * 2];
  518. short* ibeta = (short*)(buf + w + h + w); //new short[h * 2];
  519. float fx;
  520. float fy;
  521. int sx;
  522. int sy;
  523. #define SATURATE_CAST_SHORT(X) (short)::std::min(::std::max((int)(X + (X >= 0.f ? 0.5f : -0.5f)), SHRT_MIN), SHRT_MAX);
  524. for (int dx = 0; dx < w; dx++)
  525. {
  526. fx = (float)((dx + 0.5) * scale_x - 0.5);
  527. sx = static_cast<int>(floor(fx));
  528. fx -= sx;
  529. if (sx < 0)
  530. {
  531. sx = 0;
  532. fx = 0.f;
  533. }
  534. if (sx >= srcw - 1)
  535. {
  536. sx = srcw - 2;
  537. fx = 1.f;
  538. }
  539. xofs[dx] = sx * 3;
  540. float a0 = (1.f - fx) * INTER_RESIZE_COEF_SCALE;
  541. float a1 = fx * INTER_RESIZE_COEF_SCALE;
  542. ialpha[dx * 2] = SATURATE_CAST_SHORT(a0);
  543. ialpha[dx * 2 + 1] = SATURATE_CAST_SHORT(a1);
  544. }
  545. for (int dy = 0; dy < h; dy++)
  546. {
  547. fy = (float)((dy + 0.5) * scale_y - 0.5);
  548. sy = static_cast<int>(floor(fy));
  549. fy -= sy;
  550. if (sy < 0)
  551. {
  552. sy = 0;
  553. fy = 0.f;
  554. }
  555. if (sy >= srch - 1)
  556. {
  557. sy = srch - 2;
  558. fy = 1.f;
  559. }
  560. yofs[dy] = sy;
  561. float b0 = (1.f - fy) * INTER_RESIZE_COEF_SCALE;
  562. float b1 = fy * INTER_RESIZE_COEF_SCALE;
  563. ibeta[dy * 2] = SATURATE_CAST_SHORT(b0);
  564. ibeta[dy * 2 + 1] = SATURATE_CAST_SHORT(b1);
  565. }
  566. #undef SATURATE_CAST_SHORT
  567. // loop body
  568. Mat rowsbuf0(w * 3 + 1, (size_t)2u);
  569. Mat rowsbuf1(w * 3 + 1, (size_t)2u);
  570. short* rows0 = (short*)rowsbuf0.data;
  571. short* rows1 = (short*)rowsbuf1.data;
  572. int prev_sy1 = -2;
  573. for (int dy = 0; dy < h; dy++)
  574. {
  575. sy = yofs[dy];
  576. if (sy == prev_sy1)
  577. {
  578. // reuse all rows
  579. }
  580. else if (sy == prev_sy1 + 1)
  581. {
  582. // hresize one row
  583. short* rows0_old = rows0;
  584. rows0 = rows1;
  585. rows1 = rows0_old;
  586. const unsigned char* S1 = src + srcstride * (sy + 1);
  587. const short* ialphap = ialpha;
  588. short* rows1p = rows1;
  589. for (int dx = 0; dx < w; dx++)
  590. {
  591. sx = xofs[dx];
  592. short a0 = ialphap[0];
  593. short a1 = ialphap[1];
  594. const unsigned char* S1p = S1 + sx;
  595. #if __ARM_NEON
  596. int16x4_t _a0 = vdup_n_s16(a0);
  597. int16x4_t _a1 = vdup_n_s16(a1);
  598. uint8x8_t _S1 = uint8x8_t();
  599. _S1 = vld1_lane_u8(S1p, _S1, 0);
  600. _S1 = vld1_lane_u8(S1p + 1, _S1, 1);
  601. _S1 = vld1_lane_u8(S1p + 2, _S1, 2);
  602. _S1 = vld1_lane_u8(S1p + 3, _S1, 3);
  603. _S1 = vld1_lane_u8(S1p + 4, _S1, 4);
  604. _S1 = vld1_lane_u8(S1p + 5, _S1, 5);
  605. int16x8_t _S116 = vreinterpretq_s16_u16(vmovl_u8(_S1));
  606. int16x4_t _S1low = vget_low_s16(_S116);
  607. int16x4_t _S1high = vext_s16(_S1low, vget_high_s16(_S116), 3);
  608. int32x4_t _rows1 = vmull_s16(_S1low, _a0);
  609. _rows1 = vmlal_s16(_rows1, _S1high, _a1);
  610. int16x4_t _rows1_sr4 = vshrn_n_s32(_rows1, 4);
  611. vst1_s16(rows1p, _rows1_sr4);
  612. #else
  613. rows1p[0] = (S1p[0] * a0 + S1p[3] * a1) >> 4;
  614. rows1p[1] = (S1p[1] * a0 + S1p[4] * a1) >> 4;
  615. rows1p[2] = (S1p[2] * a0 + S1p[5] * a1) >> 4;
  616. #endif // __ARM_NEON
  617. ialphap += 2;
  618. rows1p += 3;
  619. }
  620. }
  621. else
  622. {
  623. // hresize two rows
  624. const unsigned char* S0 = src + srcstride * (sy);
  625. const unsigned char* S1 = src + srcstride * (sy + 1);
  626. const short* ialphap = ialpha;
  627. short* rows0p = rows0;
  628. short* rows1p = rows1;
  629. for (int dx = 0; dx < w; dx++)
  630. {
  631. sx = xofs[dx];
  632. short a0 = ialphap[0];
  633. short a1 = ialphap[1];
  634. const unsigned char* S0p = S0 + sx;
  635. const unsigned char* S1p = S1 + sx;
  636. #if __ARM_NEON
  637. int16x4_t _a0 = vdup_n_s16(a0);
  638. int16x4_t _a1 = vdup_n_s16(a1);
  639. uint8x8_t _S0 = uint8x8_t();
  640. uint8x8_t _S1 = uint8x8_t();
  641. _S0 = vld1_lane_u8(S0p, _S0, 0);
  642. _S0 = vld1_lane_u8(S0p + 1, _S0, 1);
  643. _S0 = vld1_lane_u8(S0p + 2, _S0, 2);
  644. _S0 = vld1_lane_u8(S0p + 3, _S0, 3);
  645. _S0 = vld1_lane_u8(S0p + 4, _S0, 4);
  646. _S0 = vld1_lane_u8(S0p + 5, _S0, 5);
  647. _S1 = vld1_lane_u8(S1p, _S1, 0);
  648. _S1 = vld1_lane_u8(S1p + 1, _S1, 1);
  649. _S1 = vld1_lane_u8(S1p + 2, _S1, 2);
  650. _S1 = vld1_lane_u8(S1p + 3, _S1, 3);
  651. _S1 = vld1_lane_u8(S1p + 4, _S1, 4);
  652. _S1 = vld1_lane_u8(S1p + 5, _S1, 5);
  653. int16x8_t _S016 = vreinterpretq_s16_u16(vmovl_u8(_S0));
  654. int16x8_t _S116 = vreinterpretq_s16_u16(vmovl_u8(_S1));
  655. int16x4_t _S0low = vget_low_s16(_S016);
  656. int16x4_t _S1low = vget_low_s16(_S116);
  657. int16x4_t _S0high = vext_s16(_S0low, vget_high_s16(_S016), 3);
  658. int16x4_t _S1high = vext_s16(_S1low, vget_high_s16(_S116), 3);
  659. int32x4_t _rows0 = vmull_s16(_S0low, _a0);
  660. int32x4_t _rows1 = vmull_s16(_S1low, _a0);
  661. _rows0 = vmlal_s16(_rows0, _S0high, _a1);
  662. _rows1 = vmlal_s16(_rows1, _S1high, _a1);
  663. int16x4_t _rows0_sr4 = vshrn_n_s32(_rows0, 4);
  664. int16x4_t _rows1_sr4 = vshrn_n_s32(_rows1, 4);
  665. vst1_s16(rows0p, _rows0_sr4);
  666. vst1_s16(rows1p, _rows1_sr4);
  667. #else
  668. rows0p[0] = (S0p[0] * a0 + S0p[3] * a1) >> 4;
  669. rows0p[1] = (S0p[1] * a0 + S0p[4] * a1) >> 4;
  670. rows0p[2] = (S0p[2] * a0 + S0p[5] * a1) >> 4;
  671. rows1p[0] = (S1p[0] * a0 + S1p[3] * a1) >> 4;
  672. rows1p[1] = (S1p[1] * a0 + S1p[4] * a1) >> 4;
  673. rows1p[2] = (S1p[2] * a0 + S1p[5] * a1) >> 4;
  674. #endif // __ARM_NEON
  675. ialphap += 2;
  676. rows0p += 3;
  677. rows1p += 3;
  678. }
  679. }
  680. prev_sy1 = sy;
  681. if (dy + 1 < h && yofs[dy + 1] == sy)
  682. {
  683. // vresize for two rows
  684. unsigned char* Dp0 = dst + stride * dy;
  685. unsigned char* Dp1 = dst + stride * (dy + 1);
  686. vresize_two(rows0, rows1, w * 3, Dp0, Dp1, ibeta[0], ibeta[1], ibeta[2], ibeta[3]);
  687. ibeta += 4;
  688. dy += 1;
  689. }
  690. else
  691. {
  692. // vresize
  693. unsigned char* Dp = dst + stride * dy;
  694. vresize_one(rows0, rows1, w * 3, Dp, ibeta[0], ibeta[1]);
  695. ibeta += 2;
  696. }
  697. }
  698. delete[] buf;
  699. }
  700. void resize_bilinear_c4(const unsigned char* src, int srcw, int srch, int srcstride, unsigned char* dst, int w, int h, int stride)
  701. {
  702. const int INTER_RESIZE_COEF_BITS = 11;
  703. const int INTER_RESIZE_COEF_SCALE = 1 << INTER_RESIZE_COEF_BITS;
  704. double scale_x = (double)srcw / w;
  705. double scale_y = (double)srch / h;
  706. int* buf = new int[w + h + w + h];
  707. int* xofs = buf; //new int[w];
  708. int* yofs = buf + w; //new int[h];
  709. short* ialpha = (short*)(buf + w + h); //new short[w * 2];
  710. short* ibeta = (short*)(buf + w + h + w); //new short[h * 2];
  711. float fx;
  712. float fy;
  713. int sx;
  714. int sy;
  715. #define SATURATE_CAST_SHORT(X) (short)::std::min(::std::max((int)(X + (X >= 0.f ? 0.5f : -0.5f)), SHRT_MIN), SHRT_MAX);
  716. for (int dx = 0; dx < w; dx++)
  717. {
  718. fx = (float)((dx + 0.5) * scale_x - 0.5);
  719. sx = static_cast<int>(floor(fx));
  720. fx -= sx;
  721. if (sx < 0)
  722. {
  723. sx = 0;
  724. fx = 0.f;
  725. }
  726. if (sx >= srcw - 1)
  727. {
  728. sx = srcw - 2;
  729. fx = 1.f;
  730. }
  731. xofs[dx] = sx * 4;
  732. float a0 = (1.f - fx) * INTER_RESIZE_COEF_SCALE;
  733. float a1 = fx * INTER_RESIZE_COEF_SCALE;
  734. ialpha[dx * 2] = SATURATE_CAST_SHORT(a0);
  735. ialpha[dx * 2 + 1] = SATURATE_CAST_SHORT(a1);
  736. }
  737. for (int dy = 0; dy < h; dy++)
  738. {
  739. fy = (float)((dy + 0.5) * scale_y - 0.5);
  740. sy = static_cast<int>(floor(fy));
  741. fy -= sy;
  742. if (sy < 0)
  743. {
  744. sy = 0;
  745. fy = 0.f;
  746. }
  747. if (sy >= srch - 1)
  748. {
  749. sy = srch - 2;
  750. fy = 1.f;
  751. }
  752. yofs[dy] = sy;
  753. float b0 = (1.f - fy) * INTER_RESIZE_COEF_SCALE;
  754. float b1 = fy * INTER_RESIZE_COEF_SCALE;
  755. ibeta[dy * 2] = SATURATE_CAST_SHORT(b0);
  756. ibeta[dy * 2 + 1] = SATURATE_CAST_SHORT(b1);
  757. }
  758. #undef SATURATE_CAST_SHORT
  759. // loop body
  760. Mat rowsbuf0(w * 4, (size_t)2u);
  761. Mat rowsbuf1(w * 4, (size_t)2u);
  762. short* rows0 = (short*)rowsbuf0.data;
  763. short* rows1 = (short*)rowsbuf1.data;
  764. int prev_sy1 = -2;
  765. for (int dy = 0; dy < h; dy++)
  766. {
  767. sy = yofs[dy];
  768. if (sy == prev_sy1)
  769. {
  770. // reuse all rows
  771. }
  772. else if (sy == prev_sy1 + 1)
  773. {
  774. // hresize one row
  775. short* rows0_old = rows0;
  776. rows0 = rows1;
  777. rows1 = rows0_old;
  778. const unsigned char* S1 = src + srcstride * (sy + 1);
  779. const short* ialphap = ialpha;
  780. short* rows1p = rows1;
  781. for (int dx = 0; dx < w; dx++)
  782. {
  783. sx = xofs[dx];
  784. short a0 = ialphap[0];
  785. short a1 = ialphap[1];
  786. const unsigned char* S1p = S1 + sx;
  787. #if __ARM_NEON
  788. int16x4_t _a0 = vdup_n_s16(a0);
  789. int16x4_t _a1 = vdup_n_s16(a1);
  790. uint8x8_t _S1 = vld1_u8(S1p);
  791. int16x8_t _S116 = vreinterpretq_s16_u16(vmovl_u8(_S1));
  792. int16x4_t _S1low = vget_low_s16(_S116);
  793. int16x4_t _S1high = vget_high_s16(_S116);
  794. int32x4_t _rows1 = vmull_s16(_S1low, _a0);
  795. _rows1 = vmlal_s16(_rows1, _S1high, _a1);
  796. int16x4_t _rows1_sr4 = vshrn_n_s32(_rows1, 4);
  797. vst1_s16(rows1p, _rows1_sr4);
  798. #else
  799. rows1p[0] = (S1p[0] * a0 + S1p[4] * a1) >> 4;
  800. rows1p[1] = (S1p[1] * a0 + S1p[5] * a1) >> 4;
  801. rows1p[2] = (S1p[2] * a0 + S1p[6] * a1) >> 4;
  802. rows1p[3] = (S1p[3] * a0 + S1p[7] * a1) >> 4;
  803. #endif // __ARM_NEON
  804. ialphap += 2;
  805. rows1p += 4;
  806. }
  807. }
  808. else
  809. {
  810. // hresize two rows
  811. const unsigned char* S0 = src + srcstride * (sy);
  812. const unsigned char* S1 = src + srcstride * (sy + 1);
  813. const short* ialphap = ialpha;
  814. short* rows0p = rows0;
  815. short* rows1p = rows1;
  816. for (int dx = 0; dx < w; dx++)
  817. {
  818. sx = xofs[dx];
  819. short a0 = ialphap[0];
  820. short a1 = ialphap[1];
  821. const unsigned char* S0p = S0 + sx;
  822. const unsigned char* S1p = S1 + sx;
  823. #if __ARM_NEON
  824. int16x4_t _a0 = vdup_n_s16(a0);
  825. int16x4_t _a1 = vdup_n_s16(a1);
  826. uint8x8_t _S0 = vld1_u8(S0p);
  827. uint8x8_t _S1 = vld1_u8(S1p);
  828. int16x8_t _S016 = vreinterpretq_s16_u16(vmovl_u8(_S0));
  829. int16x8_t _S116 = vreinterpretq_s16_u16(vmovl_u8(_S1));
  830. int16x4_t _S0low = vget_low_s16(_S016);
  831. int16x4_t _S1low = vget_low_s16(_S116);
  832. int16x4_t _S0high = vget_high_s16(_S016);
  833. int16x4_t _S1high = vget_high_s16(_S116);
  834. int32x4_t _rows0 = vmull_s16(_S0low, _a0);
  835. int32x4_t _rows1 = vmull_s16(_S1low, _a0);
  836. _rows0 = vmlal_s16(_rows0, _S0high, _a1);
  837. _rows1 = vmlal_s16(_rows1, _S1high, _a1);
  838. int16x4_t _rows0_sr4 = vshrn_n_s32(_rows0, 4);
  839. int16x4_t _rows1_sr4 = vshrn_n_s32(_rows1, 4);
  840. vst1_s16(rows0p, _rows0_sr4);
  841. vst1_s16(rows1p, _rows1_sr4);
  842. #else
  843. rows0p[0] = (S0p[0] * a0 + S0p[4] * a1) >> 4;
  844. rows0p[1] = (S0p[1] * a0 + S0p[5] * a1) >> 4;
  845. rows0p[2] = (S0p[2] * a0 + S0p[6] * a1) >> 4;
  846. rows0p[3] = (S0p[3] * a0 + S0p[7] * a1) >> 4;
  847. rows1p[0] = (S1p[0] * a0 + S1p[4] * a1) >> 4;
  848. rows1p[1] = (S1p[1] * a0 + S1p[5] * a1) >> 4;
  849. rows1p[2] = (S1p[2] * a0 + S1p[6] * a1) >> 4;
  850. rows1p[3] = (S1p[3] * a0 + S1p[7] * a1) >> 4;
  851. #endif // __ARM_NEON
  852. ialphap += 2;
  853. rows0p += 4;
  854. rows1p += 4;
  855. }
  856. }
  857. prev_sy1 = sy;
  858. if (dy + 1 < h && yofs[dy + 1] == sy)
  859. {
  860. // vresize for two rows
  861. unsigned char* Dp0 = dst + stride * dy;
  862. unsigned char* Dp1 = dst + stride * (dy + 1);
  863. vresize_two(rows0, rows1, w * 4, Dp0, Dp1, ibeta[0], ibeta[1], ibeta[2], ibeta[3]);
  864. ibeta += 4;
  865. dy += 1;
  866. }
  867. else
  868. {
  869. // vresize
  870. unsigned char* Dp = dst + stride * dy;
  871. vresize_one(rows0, rows1, w * 4, Dp, ibeta[0], ibeta[1]);
  872. ibeta += 2;
  873. }
  874. }
  875. delete[] buf;
  876. }
  877. void resize_bilinear_yuv420sp(const unsigned char* src, int srcw, int srch, unsigned char* dst, int w, int h)
  878. {
  879. // assert srcw % 2 == 0
  880. // assert srch % 2 == 0
  881. // assert w % 2 == 0
  882. // assert h % 2 == 0
  883. const unsigned char* srcY = src;
  884. unsigned char* dstY = dst;
  885. resize_bilinear_c1(srcY, srcw, srch, dstY, w, h);
  886. const unsigned char* srcUV = src + srcw * srch;
  887. unsigned char* dstUV = dst + w * h;
  888. resize_bilinear_c2(srcUV, srcw / 2, srch / 2, dstUV, w / 2, h / 2);
  889. }
  890. #endif // NCNN_PIXEL
  891. } // namespace ncnn