SDL_stretch.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #include "SDL_blit.h"
  20. static int SDL_LowerSoftStretchNearest(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect);
  21. static int SDL_LowerSoftStretchLinear(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect);
  22. static int SDL_UpperSoftStretch(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode);
  23. int SDL_SoftStretch(SDL_Surface *src, const SDL_Rect *srcrect,
  24. SDL_Surface *dst, const SDL_Rect *dstrect)
  25. {
  26. return SDL_UpperSoftStretch(src, srcrect, dst, dstrect, SDL_SCALEMODE_NEAREST);
  27. }
  28. int SDL_SoftStretchLinear(SDL_Surface *src, const SDL_Rect *srcrect,
  29. SDL_Surface *dst, const SDL_Rect *dstrect)
  30. {
  31. return SDL_UpperSoftStretch(src, srcrect, dst, dstrect, SDL_SCALEMODE_LINEAR);
  32. }
  33. static int SDL_UpperSoftStretch(SDL_Surface *src, const SDL_Rect *srcrect,
  34. SDL_Surface *dst, const SDL_Rect *dstrect, SDL_ScaleMode scaleMode)
  35. {
  36. int ret;
  37. int src_locked;
  38. int dst_locked;
  39. SDL_Rect full_src;
  40. SDL_Rect full_dst;
  41. if (src->format->format != dst->format->format) {
  42. return SDL_SetError("Only works with same format surfaces");
  43. }
  44. if (scaleMode != SDL_SCALEMODE_NEAREST) {
  45. if (src->format->BytesPerPixel != 4 || src->format->format == SDL_PIXELFORMAT_ARGB2101010) {
  46. return SDL_SetError("Wrong format");
  47. }
  48. }
  49. /* Verify the blit rectangles */
  50. if (srcrect) {
  51. if ((srcrect->x < 0) || (srcrect->y < 0) ||
  52. ((srcrect->x + srcrect->w) > src->w) ||
  53. ((srcrect->y + srcrect->h) > src->h)) {
  54. return SDL_SetError("Invalid source blit rectangle");
  55. }
  56. } else {
  57. full_src.x = 0;
  58. full_src.y = 0;
  59. full_src.w = src->w;
  60. full_src.h = src->h;
  61. srcrect = &full_src;
  62. }
  63. if (dstrect) {
  64. if ((dstrect->x < 0) || (dstrect->y < 0) ||
  65. ((dstrect->x + dstrect->w) > dst->w) ||
  66. ((dstrect->y + dstrect->h) > dst->h)) {
  67. return SDL_SetError("Invalid destination blit rectangle");
  68. }
  69. } else {
  70. full_dst.x = 0;
  71. full_dst.y = 0;
  72. full_dst.w = dst->w;
  73. full_dst.h = dst->h;
  74. dstrect = &full_dst;
  75. }
  76. if (dstrect->w <= 0 || dstrect->h <= 0) {
  77. return 0;
  78. }
  79. if (srcrect->w > SDL_MAX_UINT16 || srcrect->h > SDL_MAX_UINT16 ||
  80. dstrect->w > SDL_MAX_UINT16 || dstrect->h > SDL_MAX_UINT16) {
  81. return SDL_SetError("Size too large for scaling");
  82. }
  83. /* Lock the destination if it's in hardware */
  84. dst_locked = 0;
  85. if (SDL_MUSTLOCK(dst)) {
  86. if (SDL_LockSurface(dst) < 0) {
  87. return SDL_SetError("Unable to lock destination surface");
  88. }
  89. dst_locked = 1;
  90. }
  91. /* Lock the source if it's in hardware */
  92. src_locked = 0;
  93. if (SDL_MUSTLOCK(src)) {
  94. if (SDL_LockSurface(src) < 0) {
  95. if (dst_locked) {
  96. SDL_UnlockSurface(dst);
  97. }
  98. return SDL_SetError("Unable to lock source surface");
  99. }
  100. src_locked = 1;
  101. }
  102. if (scaleMode == SDL_SCALEMODE_NEAREST) {
  103. ret = SDL_LowerSoftStretchNearest(src, srcrect, dst, dstrect);
  104. } else {
  105. ret = SDL_LowerSoftStretchLinear(src, srcrect, dst, dstrect);
  106. }
  107. /* We need to unlock the surfaces if they're locked */
  108. if (dst_locked) {
  109. SDL_UnlockSurface(dst);
  110. }
  111. if (src_locked) {
  112. SDL_UnlockSurface(src);
  113. }
  114. return ret;
  115. }
  116. /* bilinear interpolation precision must be < 8
  117. Because with SSE: add-multiply: _mm_madd_epi16 works with signed int
  118. so pixels 0xb1...... are negatives and false the result
  119. same in NEON probably */
  120. #define PRECISION 7
  121. #define FIXED_POINT(i) ((Uint32)(i) << 16)
  122. #define SRC_INDEX(fp) ((Uint32)(fp) >> 16)
  123. #define INTEGER(fp) ((Uint32)(fp) >> PRECISION)
  124. #define FRAC(fp) ((Uint32)((fp) >> (16 - PRECISION)) & ((1 << PRECISION) - 1))
  125. #define FRAC_ZERO 0
  126. #define FRAC_ONE (1 << PRECISION)
  127. #define FP_ONE FIXED_POINT(1)
  128. #define BILINEAR___START \
  129. int i; \
  130. int fp_sum_h, fp_step_h, left_pad_h, right_pad_h; \
  131. int fp_sum_w, fp_step_w, left_pad_w, right_pad_w; \
  132. int fp_sum_w_init, left_pad_w_init, right_pad_w_init, dst_gap, middle_init; \
  133. get_scaler_datas(src_h, dst_h, &fp_sum_h, &fp_step_h, &left_pad_h, &right_pad_h); \
  134. get_scaler_datas(src_w, dst_w, &fp_sum_w, &fp_step_w, &left_pad_w, &right_pad_w); \
  135. fp_sum_w_init = fp_sum_w + left_pad_w * fp_step_w; \
  136. left_pad_w_init = left_pad_w; \
  137. right_pad_w_init = right_pad_w; \
  138. dst_gap = dst_pitch - 4 * dst_w; \
  139. middle_init = dst_w - left_pad_w - right_pad_w;
  140. #define BILINEAR___HEIGHT \
  141. int index_h, frac_h0, frac_h1, middle; \
  142. const Uint32 *src_h0, *src_h1; \
  143. int no_padding, incr_h0, incr_h1; \
  144. \
  145. no_padding = !(i < left_pad_h || i > dst_h - 1 - right_pad_h); \
  146. index_h = SRC_INDEX(fp_sum_h); \
  147. frac_h0 = FRAC(fp_sum_h); \
  148. \
  149. index_h = no_padding ? index_h : (i < left_pad_h ? 0 : src_h - 1); \
  150. frac_h0 = no_padding ? frac_h0 : 0; \
  151. incr_h1 = no_padding ? src_pitch : 0; \
  152. incr_h0 = index_h * src_pitch; \
  153. \
  154. src_h0 = (const Uint32 *)((const Uint8 *)src + incr_h0); \
  155. src_h1 = (const Uint32 *)((const Uint8 *)src_h0 + incr_h1); \
  156. \
  157. fp_sum_h += fp_step_h; \
  158. \
  159. frac_h1 = FRAC_ONE - frac_h0; \
  160. fp_sum_w = fp_sum_w_init; \
  161. right_pad_w = right_pad_w_init; \
  162. left_pad_w = left_pad_w_init; \
  163. middle = middle_init;
  164. #ifdef __clang__
  165. // Remove inlining of this function
  166. // Compiler crash with clang 9.0.8 / android-ndk-r21d
  167. // Compiler crash with clang 11.0.3 / Xcode
  168. // OK with clang 11.0.5 / android-ndk-22
  169. // OK with clang 12.0.0 / Xcode
  170. __attribute__((noinline))
  171. #endif
  172. static void get_scaler_datas(int src_nb, int dst_nb, int *fp_start, int *fp_step, int *left_pad, int *right_pad)
  173. {
  174. int step = FIXED_POINT(src_nb) / (dst_nb); /* source step in fixed point */
  175. int x0 = FP_ONE / 2; /* dst first pixel center at 0.5 in fixed point */
  176. int fp_sum;
  177. int i;
  178. #if 0
  179. /* scale to source coordinates */
  180. x0 *= src_nb;
  181. x0 /= dst_nb; /* x0 == step / 2 */
  182. #else
  183. /* Use this code for perfect match with pixman */
  184. Sint64 tmp[2];
  185. tmp[0] = (Sint64)step * (x0 >> 16);
  186. tmp[1] = (Sint64)step * (x0 & 0xFFFF);
  187. x0 = (int)(tmp[0] + ((tmp[1] + 0x8000) >> 16)); /* x0 == (step + 1) / 2 */
  188. #endif
  189. /* -= 0.5, get back the pixel origin, in source coordinates */
  190. x0 -= FP_ONE / 2;
  191. *fp_start = x0;
  192. *fp_step = step;
  193. *left_pad = 0;
  194. *right_pad = 0;
  195. fp_sum = x0;
  196. for (i = 0; i < dst_nb; i++) {
  197. if (fp_sum < 0) {
  198. *left_pad += 1;
  199. } else {
  200. int index = SRC_INDEX(fp_sum);
  201. if (index > src_nb - 2) {
  202. *right_pad += 1;
  203. }
  204. }
  205. fp_sum += step;
  206. }
  207. // SDL_Log("%d -> %d x0=%d step=%d left_pad=%d right_pad=%d", src_nb, dst_nb, *fp_start, *fp_step, *left_pad, *right_pad);
  208. }
  209. typedef struct color_t
  210. {
  211. Uint8 a;
  212. Uint8 b;
  213. Uint8 c;
  214. Uint8 d;
  215. } color_t;
  216. #if 0
  217. static void printf_64(const char *str, void *var)
  218. {
  219. uint8_t *val = (uint8_t*) var;
  220. printf(" * %s: %02x %02x %02x %02x _ %02x %02x %02x %02x\n",
  221. str, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
  222. }
  223. #endif
  224. /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
  225. static SDL_INLINE void INTERPOL(const Uint32 *src_x0, const Uint32 *src_x1, int frac0, int frac1, Uint32 *dst)
  226. {
  227. const color_t *c0 = (const color_t *)src_x0;
  228. const color_t *c1 = (const color_t *)src_x1;
  229. color_t *cx = (color_t *)dst;
  230. #if 0
  231. cx->a = c0->a + INTEGER(frac0 * (c1->a - c0->a));
  232. cx->b = c0->b + INTEGER(frac0 * (c1->b - c0->b));
  233. cx->c = c0->c + INTEGER(frac0 * (c1->c - c0->c));
  234. cx->d = c0->d + INTEGER(frac0 * (c1->d - c0->d));
  235. #else
  236. cx->a = (Uint8)INTEGER(frac1 * c0->a + frac0 * c1->a);
  237. cx->b = (Uint8)INTEGER(frac1 * c0->b + frac0 * c1->b);
  238. cx->c = (Uint8)INTEGER(frac1 * c0->c + frac0 * c1->c);
  239. cx->d = (Uint8)INTEGER(frac1 * c0->d + frac0 * c1->d);
  240. #endif
  241. }
  242. static SDL_INLINE void INTERPOL_BILINEAR(const Uint32 *s0, const Uint32 *s1, int frac_w0, int frac_h0, int frac_h1, Uint32 *dst)
  243. {
  244. Uint32 tmp[2];
  245. unsigned int frac_w1 = FRAC_ONE - frac_w0;
  246. /* Vertical first, store to 'tmp' */
  247. INTERPOL(s0, s1, frac_h0, frac_h1, tmp);
  248. INTERPOL(s0 + 1, s1 + 1, frac_h0, frac_h1, tmp + 1);
  249. /* Horizontal, store to 'dst' */
  250. INTERPOL(tmp, tmp + 1, frac_w0, frac_w1, dst);
  251. }
  252. static int scale_mat(const Uint32 *src, int src_w, int src_h, int src_pitch,
  253. Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  254. {
  255. BILINEAR___START
  256. for (i = 0; i < dst_h; i++) {
  257. BILINEAR___HEIGHT
  258. while (left_pad_w--) {
  259. INTERPOL_BILINEAR(src_h0, src_h1, FRAC_ZERO, frac_h0, frac_h1, dst);
  260. dst += 1;
  261. }
  262. while (middle--) {
  263. const Uint32 *s_00_01;
  264. const Uint32 *s_10_11;
  265. int index_w = 4 * SRC_INDEX(fp_sum_w);
  266. int frac_w = FRAC(fp_sum_w);
  267. fp_sum_w += fp_step_w;
  268. /*
  269. x00 ... x0_ ..... x01
  270. . . .
  271. . x .
  272. . . .
  273. . . .
  274. x10 ... x1_ ..... x11
  275. */
  276. s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
  277. s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
  278. INTERPOL_BILINEAR(s_00_01, s_10_11, frac_w, frac_h0, frac_h1, dst);
  279. dst += 1;
  280. }
  281. while (right_pad_w--) {
  282. int index_w = 4 * (src_w - 2);
  283. const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
  284. const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
  285. INTERPOL_BILINEAR(s_00_01, s_10_11, FRAC_ONE, frac_h0, frac_h1, dst);
  286. dst += 1;
  287. }
  288. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  289. }
  290. return 0;
  291. }
  292. #ifdef SDL_NEON_INTRINSICS
  293. #define CAST_uint8x8_t (uint8x8_t)
  294. #define CAST_uint32x2_t (uint32x2_t)
  295. #endif
  296. #if defined(__WINRT__) || defined(_MSC_VER)
  297. #ifdef SDL_NEON_INTRINSICS
  298. #undef CAST_uint8x8_t
  299. #undef CAST_uint32x2_t
  300. #define CAST_uint8x8_t
  301. #define CAST_uint32x2_t
  302. #endif
  303. #endif
  304. #ifdef SDL_SSE2_INTRINSICS
  305. #if 0
  306. static void SDL_TARGETING("sse2") printf_128(const char *str, __m128i var)
  307. {
  308. uint16_t *val = (uint16_t*) &var;
  309. printf(" * %s: %04x %04x %04x %04x _ %04x %04x %04x %04x\n",
  310. str, val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]);
  311. }
  312. #endif
  313. static SDL_INLINE int hasSSE2(void)
  314. {
  315. static int val = -1;
  316. if (val != -1) {
  317. return val;
  318. }
  319. val = SDL_HasSSE2();
  320. return val;
  321. }
  322. static SDL_INLINE void SDL_TARGETING("sse2") INTERPOL_BILINEAR_SSE(const Uint32 *s0, const Uint32 *s1, int frac_w, __m128i v_frac_h0, __m128i v_frac_h1, Uint32 *dst, __m128i zero)
  323. {
  324. __m128i x_00_01, x_10_11; /* Pixels in 4*uint8 in row */
  325. __m128i v_frac_w0, k0, l0, d0, e0;
  326. int f, f2;
  327. f = frac_w;
  328. f2 = FRAC_ONE - frac_w;
  329. v_frac_w0 = _mm_set_epi16((short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2);
  330. x_00_01 = _mm_loadl_epi64((const __m128i *)s0); /* Load x00 and x01 */
  331. x_10_11 = _mm_loadl_epi64((const __m128i *)s1);
  332. /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
  333. /* Interpolation vertical */
  334. k0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_00_01, zero), v_frac_h1);
  335. l0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_10_11, zero), v_frac_h0);
  336. k0 = _mm_add_epi16(k0, l0);
  337. /* For perfect match, clear the factionnal part eventually. */
  338. /*
  339. k0 = _mm_srli_epi16(k0, PRECISION);
  340. k0 = _mm_slli_epi16(k0, PRECISION);
  341. */
  342. /* Interpolation horizontal */
  343. l0 = _mm_unpacklo_epi64(/* unused */ l0, k0);
  344. k0 = _mm_madd_epi16(_mm_unpackhi_epi16(l0, k0), v_frac_w0);
  345. /* Store 1 pixel */
  346. d0 = _mm_srli_epi32(k0, PRECISION * 2);
  347. e0 = _mm_packs_epi32(d0, d0);
  348. e0 = _mm_packus_epi16(e0, e0);
  349. *dst = _mm_cvtsi128_si32(e0);
  350. }
  351. static int SDL_TARGETING("sse2") scale_mat_SSE(const Uint32 *src, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  352. {
  353. BILINEAR___START
  354. for (i = 0; i < dst_h; i++) {
  355. int nb_block2;
  356. __m128i v_frac_h0;
  357. __m128i v_frac_h1;
  358. __m128i zero;
  359. BILINEAR___HEIGHT
  360. nb_block2 = middle / 2;
  361. v_frac_h0 = _mm_set_epi16((short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0, (short)frac_h0);
  362. v_frac_h1 = _mm_set_epi16((short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1, (short)frac_h1);
  363. zero = _mm_setzero_si128();
  364. while (left_pad_w--) {
  365. INTERPOL_BILINEAR_SSE(src_h0, src_h1, FRAC_ZERO, v_frac_h0, v_frac_h1, dst, zero);
  366. dst += 1;
  367. }
  368. while (nb_block2--) {
  369. int index_w_0, frac_w_0;
  370. int index_w_1, frac_w_1;
  371. const Uint32 *s_00_01, *s_02_03, *s_10_11, *s_12_13;
  372. __m128i x_00_01, x_10_11, x_02_03, x_12_13; /* Pixels in 4*uint8 in row */
  373. __m128i v_frac_w0, k0, l0, d0, e0;
  374. __m128i v_frac_w1, k1, l1, d1, e1;
  375. int f, f2;
  376. index_w_0 = 4 * SRC_INDEX(fp_sum_w);
  377. frac_w_0 = FRAC(fp_sum_w);
  378. fp_sum_w += fp_step_w;
  379. index_w_1 = 4 * SRC_INDEX(fp_sum_w);
  380. frac_w_1 = FRAC(fp_sum_w);
  381. fp_sum_w += fp_step_w;
  382. /*
  383. x00............ x01 x02...........x03
  384. . . . . . .
  385. j0 f0 j1 j2 f1 j3
  386. . . . . . .
  387. . . . . . .
  388. . . . . . .
  389. x10............ x11 x12...........x13
  390. */
  391. s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_0);
  392. s_02_03 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_1);
  393. s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_0);
  394. s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1);
  395. f = frac_w_0;
  396. f2 = FRAC_ONE - frac_w_0;
  397. v_frac_w0 = _mm_set_epi16((short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2);
  398. f = frac_w_1;
  399. f2 = FRAC_ONE - frac_w_1;
  400. v_frac_w1 = _mm_set_epi16((short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2, (short)f, (short)f2);
  401. x_00_01 = _mm_loadl_epi64((const __m128i *)s_00_01); /* Load x00 and x01 */
  402. x_02_03 = _mm_loadl_epi64((const __m128i *)s_02_03);
  403. x_10_11 = _mm_loadl_epi64((const __m128i *)s_10_11);
  404. x_12_13 = _mm_loadl_epi64((const __m128i *)s_12_13);
  405. /* Interpolation vertical */
  406. k0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_00_01, zero), v_frac_h1);
  407. l0 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_10_11, zero), v_frac_h0);
  408. k0 = _mm_add_epi16(k0, l0);
  409. k1 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_02_03, zero), v_frac_h1);
  410. l1 = _mm_mullo_epi16(_mm_unpacklo_epi8(x_12_13, zero), v_frac_h0);
  411. k1 = _mm_add_epi16(k1, l1);
  412. /* Interpolation horizontal */
  413. l0 = _mm_unpacklo_epi64(/* unused */ l0, k0);
  414. k0 = _mm_madd_epi16(_mm_unpackhi_epi16(l0, k0), v_frac_w0);
  415. l1 = _mm_unpacklo_epi64(/* unused */ l1, k1);
  416. k1 = _mm_madd_epi16(_mm_unpackhi_epi16(l1, k1), v_frac_w1);
  417. /* Store 1 pixel */
  418. d0 = _mm_srli_epi32(k0, PRECISION * 2);
  419. e0 = _mm_packs_epi32(d0, d0);
  420. e0 = _mm_packus_epi16(e0, e0);
  421. *dst++ = _mm_cvtsi128_si32(e0);
  422. /* Store 1 pixel */
  423. d1 = _mm_srli_epi32(k1, PRECISION * 2);
  424. e1 = _mm_packs_epi32(d1, d1);
  425. e1 = _mm_packus_epi16(e1, e1);
  426. *dst++ = _mm_cvtsi128_si32(e1);
  427. }
  428. /* Last point */
  429. if (middle & 0x1) {
  430. const Uint32 *s_00_01;
  431. const Uint32 *s_10_11;
  432. int index_w = 4 * SRC_INDEX(fp_sum_w);
  433. int frac_w = FRAC(fp_sum_w);
  434. fp_sum_w += fp_step_w;
  435. s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
  436. s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
  437. INTERPOL_BILINEAR_SSE(s_00_01, s_10_11, frac_w, v_frac_h0, v_frac_h1, dst, zero);
  438. dst += 1;
  439. }
  440. while (right_pad_w--) {
  441. int index_w = 4 * (src_w - 2);
  442. const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
  443. const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
  444. INTERPOL_BILINEAR_SSE(s_00_01, s_10_11, FRAC_ONE, v_frac_h0, v_frac_h1, dst, zero);
  445. dst += 1;
  446. }
  447. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  448. }
  449. return 0;
  450. }
  451. #endif
  452. #ifdef SDL_NEON_INTRINSICS
  453. static SDL_INLINE int hasNEON(void)
  454. {
  455. static int val = -1;
  456. if (val != -1) {
  457. return val;
  458. }
  459. val = SDL_HasNEON();
  460. return val;
  461. }
  462. static SDL_INLINE void INTERPOL_BILINEAR_NEON(const Uint32 *s0, const Uint32 *s1, int frac_w, uint8x8_t v_frac_h0, uint8x8_t v_frac_h1, Uint32 *dst)
  463. {
  464. uint8x8_t x_00_01, x_10_11; /* Pixels in 4*uint8 in row */
  465. uint16x8_t k0;
  466. uint32x4_t l0;
  467. uint16x8_t d0;
  468. uint8x8_t e0;
  469. x_00_01 = CAST_uint8x8_t vld1_u32(s0); /* Load 2 pixels */
  470. x_10_11 = CAST_uint8x8_t vld1_u32(s1);
  471. /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
  472. k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */
  473. k0 = vmlal_u8(k0, x_10_11, v_frac_h0); /* k0 += x1 * frac */
  474. /* k0 now contains 2 interpolated pixels { j0, j1 } */
  475. l0 = vshll_n_u16(vget_low_u16(k0), PRECISION);
  476. l0 = vmlsl_n_u16(l0, vget_low_u16(k0), frac_w);
  477. l0 = vmlal_n_u16(l0, vget_high_u16(k0), frac_w);
  478. /* Shift and narrow */
  479. d0 = vcombine_u16(
  480. /* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION),
  481. /* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION));
  482. /* Narrow again */
  483. e0 = vmovn_u16(d0);
  484. /* Store 1 pixel */
  485. *dst = vget_lane_u32(CAST_uint32x2_t e0, 0);
  486. }
  487. static int scale_mat_NEON(const Uint32 *src, int src_w, int src_h, int src_pitch, Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  488. {
  489. BILINEAR___START
  490. for (i = 0; i < dst_h; i++) {
  491. int nb_block4;
  492. uint8x8_t v_frac_h0, v_frac_h1;
  493. BILINEAR___HEIGHT
  494. nb_block4 = middle / 4;
  495. v_frac_h0 = vmov_n_u8(frac_h0);
  496. v_frac_h1 = vmov_n_u8(frac_h1);
  497. while (left_pad_w--) {
  498. INTERPOL_BILINEAR_NEON(src_h0, src_h1, FRAC_ZERO, v_frac_h0, v_frac_h1, dst);
  499. dst += 1;
  500. }
  501. while (nb_block4--) {
  502. int index_w_0, frac_w_0;
  503. int index_w_1, frac_w_1;
  504. int index_w_2, frac_w_2;
  505. int index_w_3, frac_w_3;
  506. const Uint32 *s_00_01, *s_02_03, *s_04_05, *s_06_07;
  507. const Uint32 *s_10_11, *s_12_13, *s_14_15, *s_16_17;
  508. uint8x8_t x_00_01, x_10_11, x_02_03, x_12_13; /* Pixels in 4*uint8 in row */
  509. uint8x8_t x_04_05, x_14_15, x_06_07, x_16_17;
  510. uint16x8_t k0, k1, k2, k3;
  511. uint32x4_t l0, l1, l2, l3;
  512. uint16x8_t d0, d1;
  513. uint8x8_t e0, e1;
  514. uint32x4_t f0;
  515. index_w_0 = 4 * SRC_INDEX(fp_sum_w);
  516. frac_w_0 = FRAC(fp_sum_w);
  517. fp_sum_w += fp_step_w;
  518. index_w_1 = 4 * SRC_INDEX(fp_sum_w);
  519. frac_w_1 = FRAC(fp_sum_w);
  520. fp_sum_w += fp_step_w;
  521. index_w_2 = 4 * SRC_INDEX(fp_sum_w);
  522. frac_w_2 = FRAC(fp_sum_w);
  523. fp_sum_w += fp_step_w;
  524. index_w_3 = 4 * SRC_INDEX(fp_sum_w);
  525. frac_w_3 = FRAC(fp_sum_w);
  526. fp_sum_w += fp_step_w;
  527. s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_0);
  528. s_02_03 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_1);
  529. s_04_05 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_2);
  530. s_06_07 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_3);
  531. s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_0);
  532. s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1);
  533. s_14_15 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_2);
  534. s_16_17 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_3);
  535. /* Interpolation vertical */
  536. x_00_01 = CAST_uint8x8_t vld1_u32(s_00_01); /* Load 2 pixels */
  537. x_02_03 = CAST_uint8x8_t vld1_u32(s_02_03);
  538. x_04_05 = CAST_uint8x8_t vld1_u32(s_04_05);
  539. x_06_07 = CAST_uint8x8_t vld1_u32(s_06_07);
  540. x_10_11 = CAST_uint8x8_t vld1_u32(s_10_11);
  541. x_12_13 = CAST_uint8x8_t vld1_u32(s_12_13);
  542. x_14_15 = CAST_uint8x8_t vld1_u32(s_14_15);
  543. x_16_17 = CAST_uint8x8_t vld1_u32(s_16_17);
  544. /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
  545. k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */
  546. k0 = vmlal_u8(k0, x_10_11, v_frac_h0); /* k0 += x1 * frac */
  547. k1 = vmull_u8(x_02_03, v_frac_h1);
  548. k1 = vmlal_u8(k1, x_12_13, v_frac_h0);
  549. k2 = vmull_u8(x_04_05, v_frac_h1);
  550. k2 = vmlal_u8(k2, x_14_15, v_frac_h0);
  551. k3 = vmull_u8(x_06_07, v_frac_h1);
  552. k3 = vmlal_u8(k3, x_16_17, v_frac_h0);
  553. /* k0 now contains 2 interpolated pixels { j0, j1 } */
  554. /* k1 now contains 2 interpolated pixels { j2, j3 } */
  555. /* k2 now contains 2 interpolated pixels { j4, j5 } */
  556. /* k3 now contains 2 interpolated pixels { j6, j7 } */
  557. l0 = vshll_n_u16(vget_low_u16(k0), PRECISION);
  558. l0 = vmlsl_n_u16(l0, vget_low_u16(k0), frac_w_0);
  559. l0 = vmlal_n_u16(l0, vget_high_u16(k0), frac_w_0);
  560. l1 = vshll_n_u16(vget_low_u16(k1), PRECISION);
  561. l1 = vmlsl_n_u16(l1, vget_low_u16(k1), frac_w_1);
  562. l1 = vmlal_n_u16(l1, vget_high_u16(k1), frac_w_1);
  563. l2 = vshll_n_u16(vget_low_u16(k2), PRECISION);
  564. l2 = vmlsl_n_u16(l2, vget_low_u16(k2), frac_w_2);
  565. l2 = vmlal_n_u16(l2, vget_high_u16(k2), frac_w_2);
  566. l3 = vshll_n_u16(vget_low_u16(k3), PRECISION);
  567. l3 = vmlsl_n_u16(l3, vget_low_u16(k3), frac_w_3);
  568. l3 = vmlal_n_u16(l3, vget_high_u16(k3), frac_w_3);
  569. /* shift and narrow */
  570. d0 = vcombine_u16(
  571. /* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION),
  572. /* uint16x4_t */ vshrn_n_u32(l1, 2 * PRECISION));
  573. /* narrow again */
  574. e0 = vmovn_u16(d0);
  575. /* Shift and narrow */
  576. d1 = vcombine_u16(
  577. /* uint16x4_t */ vshrn_n_u32(l2, 2 * PRECISION),
  578. /* uint16x4_t */ vshrn_n_u32(l3, 2 * PRECISION));
  579. /* Narrow again */
  580. e1 = vmovn_u16(d1);
  581. f0 = vcombine_u32(CAST_uint32x2_t e0, CAST_uint32x2_t e1);
  582. /* Store 4 pixels */
  583. vst1q_u32(dst, f0);
  584. dst += 4;
  585. }
  586. if (middle & 0x2) {
  587. int index_w_0, frac_w_0;
  588. int index_w_1, frac_w_1;
  589. const Uint32 *s_00_01, *s_02_03;
  590. const Uint32 *s_10_11, *s_12_13;
  591. uint8x8_t x_00_01, x_10_11, x_02_03, x_12_13; /* Pixels in 4*uint8 in row */
  592. uint16x8_t k0, k1;
  593. uint32x4_t l0, l1;
  594. uint16x8_t d0;
  595. uint8x8_t e0;
  596. index_w_0 = 4 * SRC_INDEX(fp_sum_w);
  597. frac_w_0 = FRAC(fp_sum_w);
  598. fp_sum_w += fp_step_w;
  599. index_w_1 = 4 * SRC_INDEX(fp_sum_w);
  600. frac_w_1 = FRAC(fp_sum_w);
  601. fp_sum_w += fp_step_w;
  602. /*
  603. x00............ x01 x02...........x03
  604. . . . . . .
  605. j0 dest0 j1 j2 dest1 j3
  606. . . . . . .
  607. . . . . . .
  608. . . . . . .
  609. x10............ x11 x12...........x13
  610. */
  611. s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_0);
  612. s_02_03 = (const Uint32 *)((const Uint8 *)src_h0 + index_w_1);
  613. s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_0);
  614. s_12_13 = (const Uint32 *)((const Uint8 *)src_h1 + index_w_1);
  615. /* Interpolation vertical */
  616. x_00_01 = CAST_uint8x8_t vld1_u32(s_00_01); /* Load 2 pixels */
  617. x_02_03 = CAST_uint8x8_t vld1_u32(s_02_03);
  618. x_10_11 = CAST_uint8x8_t vld1_u32(s_10_11);
  619. x_12_13 = CAST_uint8x8_t vld1_u32(s_12_13);
  620. /* Interpolated == x0 + frac * (x1 - x0) == x0 * (1 - frac) + x1 * frac */
  621. k0 = vmull_u8(x_00_01, v_frac_h1); /* k0 := x0 * (1 - frac) */
  622. k0 = vmlal_u8(k0, x_10_11, v_frac_h0); /* k0 += x1 * frac */
  623. k1 = vmull_u8(x_02_03, v_frac_h1);
  624. k1 = vmlal_u8(k1, x_12_13, v_frac_h0);
  625. /* k0 now contains 2 interpolated pixels { j0, j1 } */
  626. /* k1 now contains 2 interpolated pixels { j2, j3 } */
  627. l0 = vshll_n_u16(vget_low_u16(k0), PRECISION);
  628. l0 = vmlsl_n_u16(l0, vget_low_u16(k0), frac_w_0);
  629. l0 = vmlal_n_u16(l0, vget_high_u16(k0), frac_w_0);
  630. l1 = vshll_n_u16(vget_low_u16(k1), PRECISION);
  631. l1 = vmlsl_n_u16(l1, vget_low_u16(k1), frac_w_1);
  632. l1 = vmlal_n_u16(l1, vget_high_u16(k1), frac_w_1);
  633. /* Shift and narrow */
  634. d0 = vcombine_u16(
  635. /* uint16x4_t */ vshrn_n_u32(l0, 2 * PRECISION),
  636. /* uint16x4_t */ vshrn_n_u32(l1, 2 * PRECISION));
  637. /* Narrow again */
  638. e0 = vmovn_u16(d0);
  639. /* Store 2 pixels */
  640. vst1_u32(dst, CAST_uint32x2_t e0);
  641. dst += 2;
  642. }
  643. /* Last point */
  644. if (middle & 0x1) {
  645. int index_w = 4 * SRC_INDEX(fp_sum_w);
  646. int frac_w = FRAC(fp_sum_w);
  647. const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
  648. const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
  649. INTERPOL_BILINEAR_NEON(s_00_01, s_10_11, frac_w, v_frac_h0, v_frac_h1, dst);
  650. dst += 1;
  651. }
  652. while (right_pad_w--) {
  653. int index_w = 4 * (src_w - 2);
  654. const Uint32 *s_00_01 = (const Uint32 *)((const Uint8 *)src_h0 + index_w);
  655. const Uint32 *s_10_11 = (const Uint32 *)((const Uint8 *)src_h1 + index_w);
  656. INTERPOL_BILINEAR_NEON(s_00_01, s_10_11, FRAC_ONE, v_frac_h0, v_frac_h1, dst);
  657. dst += 1;
  658. }
  659. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  660. }
  661. return 0;
  662. }
  663. #endif
  664. int SDL_LowerSoftStretchLinear(SDL_Surface *s, const SDL_Rect *srcrect,
  665. SDL_Surface *d, const SDL_Rect *dstrect)
  666. {
  667. int ret = -1;
  668. int src_w = srcrect->w;
  669. int src_h = srcrect->h;
  670. int dst_w = dstrect->w;
  671. int dst_h = dstrect->h;
  672. int src_pitch = s->pitch;
  673. int dst_pitch = d->pitch;
  674. Uint32 *src = (Uint32 *)((Uint8 *)s->pixels + srcrect->x * 4 + srcrect->y * src_pitch);
  675. Uint32 *dst = (Uint32 *)((Uint8 *)d->pixels + dstrect->x * 4 + dstrect->y * dst_pitch);
  676. #ifdef SDL_NEON_INTRINSICS
  677. if (ret == -1 && hasNEON()) {
  678. ret = scale_mat_NEON(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  679. }
  680. #endif
  681. #ifdef SDL_SSE2_INTRINSICS
  682. if (ret == -1 && hasSSE2()) {
  683. ret = scale_mat_SSE(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  684. }
  685. #endif
  686. if (ret == -1) {
  687. ret = scale_mat(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  688. }
  689. return ret;
  690. }
  691. #define SDL_SCALE_NEAREST__START \
  692. int i; \
  693. Uint32 posy, incy; \
  694. Uint32 posx, incx; \
  695. int dst_gap; \
  696. int srcy, n; \
  697. const Uint32 *src_h0; \
  698. incy = (src_h << 16) / dst_h; \
  699. incx = (src_w << 16) / dst_w; \
  700. dst_gap = dst_pitch - bpp * dst_w; \
  701. posy = incy / 2;
  702. #define SDL_SCALE_NEAREST__HEIGHT \
  703. srcy = (posy >> 16); \
  704. src_h0 = (const Uint32 *)((const Uint8 *)src_ptr + srcy * src_pitch); \
  705. posy += incy; \
  706. posx = incx / 2; \
  707. n = dst_w;
  708. static int scale_mat_nearest_1(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch,
  709. Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  710. {
  711. Uint32 bpp = 1;
  712. SDL_SCALE_NEAREST__START
  713. for (i = 0; i < dst_h; i++) {
  714. SDL_SCALE_NEAREST__HEIGHT
  715. while (n--) {
  716. const Uint8 *src;
  717. int srcx = bpp * (posx >> 16);
  718. posx += incx;
  719. src = (const Uint8 *)src_h0 + srcx;
  720. *(Uint8 *)dst = *src;
  721. dst = (Uint32 *)((Uint8 *)dst + bpp);
  722. }
  723. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  724. }
  725. return 0;
  726. }
  727. static int scale_mat_nearest_2(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch,
  728. Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  729. {
  730. Uint32 bpp = 2;
  731. SDL_SCALE_NEAREST__START
  732. for (i = 0; i < dst_h; i++) {
  733. SDL_SCALE_NEAREST__HEIGHT
  734. while (n--) {
  735. const Uint16 *src;
  736. int srcx = bpp * (posx >> 16);
  737. posx += incx;
  738. src = (const Uint16 *)((const Uint8 *)src_h0 + srcx);
  739. *(Uint16 *)dst = *src;
  740. dst = (Uint32 *)((Uint8 *)dst + bpp);
  741. }
  742. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  743. }
  744. return 0;
  745. }
  746. static int scale_mat_nearest_3(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch,
  747. Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  748. {
  749. Uint32 bpp = 3;
  750. SDL_SCALE_NEAREST__START
  751. for (i = 0; i < dst_h; i++) {
  752. SDL_SCALE_NEAREST__HEIGHT
  753. while (n--) {
  754. const Uint8 *src;
  755. int srcx = bpp * (posx >> 16);
  756. posx += incx;
  757. src = (const Uint8 *)src_h0 + srcx;
  758. ((Uint8 *)dst)[0] = src[0];
  759. ((Uint8 *)dst)[1] = src[1];
  760. ((Uint8 *)dst)[2] = src[2];
  761. dst = (Uint32 *)((Uint8 *)dst + bpp);
  762. }
  763. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  764. }
  765. return 0;
  766. }
  767. static int scale_mat_nearest_4(const Uint32 *src_ptr, int src_w, int src_h, int src_pitch,
  768. Uint32 *dst, int dst_w, int dst_h, int dst_pitch)
  769. {
  770. Uint32 bpp = 4;
  771. SDL_SCALE_NEAREST__START
  772. for (i = 0; i < dst_h; i++) {
  773. SDL_SCALE_NEAREST__HEIGHT
  774. while (n--) {
  775. const Uint32 *src;
  776. int srcx = bpp * (posx >> 16);
  777. posx += incx;
  778. src = (const Uint32 *)((const Uint8 *)src_h0 + srcx);
  779. *dst = *src;
  780. dst = (Uint32 *)((Uint8 *)dst + bpp);
  781. }
  782. dst = (Uint32 *)((Uint8 *)dst + dst_gap);
  783. }
  784. return 0;
  785. }
  786. int SDL_LowerSoftStretchNearest(SDL_Surface *s, const SDL_Rect *srcrect,
  787. SDL_Surface *d, const SDL_Rect *dstrect)
  788. {
  789. int src_w = srcrect->w;
  790. int src_h = srcrect->h;
  791. int dst_w = dstrect->w;
  792. int dst_h = dstrect->h;
  793. int src_pitch = s->pitch;
  794. int dst_pitch = d->pitch;
  795. const int bpp = d->format->BytesPerPixel;
  796. Uint32 *src = (Uint32 *)((Uint8 *)s->pixels + srcrect->x * bpp + srcrect->y * src_pitch);
  797. Uint32 *dst = (Uint32 *)((Uint8 *)d->pixels + dstrect->x * bpp + dstrect->y * dst_pitch);
  798. if (bpp == 4) {
  799. return scale_mat_nearest_4(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  800. } else if (bpp == 3) {
  801. return scale_mat_nearest_3(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  802. } else if (bpp == 2) {
  803. return scale_mat_nearest_2(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  804. } else {
  805. return scale_mat_nearest_1(src, src_w, src_h, src_pitch, dst, dst_w, dst_h, dst_pitch);
  806. }
  807. }