graphics.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2012-2018 K. Lange
  5. *
  6. * Generic Graphics library for ToaruOS
  7. */
  8. #include <stdint.h>
  9. #include <string.h>
  10. #include <stdio.h>
  11. #include <math.h>
  12. #include <fcntl.h>
  13. #include <sys/ioctl.h>
  14. #ifndef NO_SSE
  15. #include <xmmintrin.h>
  16. #include <emmintrin.h>
  17. #endif
  18. #include <kernel/video.h>
  19. #include <toaru/graphics.h>
  20. static inline int32_t min(int32_t a, int32_t b) {
  21. return (a < b) ? a : b;
  22. }
  23. static inline int32_t max(int32_t a, int32_t b) {
  24. return (a > b) ? a : b;
  25. }
  26. static inline uint16_t min16(uint16_t a, uint16_t b) {
  27. return (a < b) ? a : b;
  28. }
  29. static inline uint16_t max16(uint16_t a, uint16_t b) {
  30. return (a > b) ? a : b;
  31. }
  32. static int _is_in_clip(gfx_context_t * ctx, int32_t y) {
  33. if (!ctx->clips) return 1;
  34. if (y < 0 || y >= ctx->clips_size) return 1;
  35. return ctx->clips[y];
  36. }
  37. void gfx_add_clip(gfx_context_t * ctx, int32_t x, int32_t y, int32_t w, int32_t h) {
  38. (void)x;
  39. (void)w; // TODO Horizontal clipping
  40. if (!ctx->clips) {
  41. ctx->clips = malloc(ctx->height);
  42. memset(ctx->clips, 0, ctx->height);
  43. ctx->clips_size = ctx->height;
  44. }
  45. for (int i = max(y,0); i < min(y+h,ctx->clips_size); ++i) {
  46. ctx->clips[i] = 1;
  47. }
  48. }
  49. void gfx_clear_clip(gfx_context_t * ctx) {
  50. if (ctx->clips) {
  51. memset(ctx->clips, 0, ctx->clips_size);
  52. }
  53. }
  54. void gfx_no_clip(gfx_context_t * ctx) {
  55. void * tmp = ctx->clips;
  56. if (!tmp) return;
  57. ctx->clips = NULL;
  58. free(tmp);
  59. }
  60. /* Pointer to graphics memory */
  61. void flip(gfx_context_t * ctx) {
  62. if (ctx->clips) {
  63. for (size_t i = 0; i < ctx->height; ++i) {
  64. if (_is_in_clip(ctx,i)) {
  65. memcpy(&ctx->buffer[i*GFX_S(ctx)], &ctx->backbuffer[i*GFX_S(ctx)], 4 * ctx->width);
  66. }
  67. }
  68. } else {
  69. memcpy(ctx->buffer, ctx->backbuffer, ctx->size);
  70. }
  71. }
  72. void clearbuffer(gfx_context_t * ctx) {
  73. memset(ctx->backbuffer, 0, ctx->size);
  74. }
  75. /* Deprecated */
  76. static int framebuffer_fd = 0;
  77. gfx_context_t * init_graphics_fullscreen() {
  78. gfx_context_t * out = malloc(sizeof(gfx_context_t));
  79. out->clips = NULL;
  80. if (!framebuffer_fd) {
  81. framebuffer_fd = open("/dev/fb0", 0, 0);
  82. }
  83. if (framebuffer_fd < 0) {
  84. /* oh shit */
  85. free(out);
  86. return NULL;
  87. }
  88. ioctl(framebuffer_fd, IO_VID_WIDTH, &out->width);
  89. ioctl(framebuffer_fd, IO_VID_HEIGHT, &out->height);
  90. ioctl(framebuffer_fd, IO_VID_DEPTH, &out->depth);
  91. ioctl(framebuffer_fd, IO_VID_STRIDE, &out->stride);
  92. ioctl(framebuffer_fd, IO_VID_ADDR, &out->buffer);
  93. ioctl(framebuffer_fd, IO_VID_SIGNAL, NULL);
  94. out->size = GFX_H(out) * GFX_S(out);
  95. out->backbuffer = out->buffer;
  96. return out;
  97. }
  98. uint32_t framebuffer_stride(void) {
  99. uint32_t stride;
  100. ioctl(framebuffer_fd, IO_VID_STRIDE, &stride);
  101. return stride;
  102. }
  103. gfx_context_t * init_graphics_fullscreen_double_buffer() {
  104. gfx_context_t * out = init_graphics_fullscreen();
  105. if (!out) return NULL;
  106. out->backbuffer = malloc(GFX_S(out) * GFX_H(out));
  107. return out;
  108. }
  109. gfx_context_t * init_graphics_subregion(gfx_context_t * base, int x, int y, int width, int height) {
  110. gfx_context_t * out = malloc(sizeof(gfx_context_t));
  111. out->clips = NULL;
  112. out->depth = 32;
  113. out->width = width;
  114. out->height = height;
  115. out->stride = base->stride;
  116. out->backbuffer = base->buffer + (base->stride * y) + x * 4;
  117. out->buffer = base->buffer + (base->stride * y) + x * 4;
  118. if (base->clips) {
  119. for (int _y = 0; _y < height; ++_y) {
  120. if (_is_in_clip(base, y + _y)) {
  121. gfx_add_clip(out,0,_y,width,1);
  122. }
  123. }
  124. }
  125. out->size = 0; /* don't allow flip or clear operations */
  126. return out;
  127. }
  128. void reinit_graphics_fullscreen(gfx_context_t * out) {
  129. ioctl(framebuffer_fd, IO_VID_WIDTH, &out->width);
  130. ioctl(framebuffer_fd, IO_VID_HEIGHT, &out->height);
  131. ioctl(framebuffer_fd, IO_VID_DEPTH, &out->depth);
  132. ioctl(framebuffer_fd, IO_VID_STRIDE, &out->stride);
  133. out->size = GFX_H(out) * GFX_S(out);
  134. if (out->clips && out->clips_size != out->height) {
  135. free(out->clips);
  136. out->clips = NULL;
  137. out->clips_size = 0;
  138. }
  139. if (out->buffer != out->backbuffer) {
  140. ioctl(framebuffer_fd, IO_VID_ADDR, &out->buffer);
  141. out->backbuffer = realloc(out->backbuffer, GFX_S(out) * GFX_H(out));
  142. } else {
  143. ioctl(framebuffer_fd, IO_VID_ADDR, &out->buffer);
  144. out->backbuffer = out->buffer;
  145. }
  146. }
  147. gfx_context_t * init_graphics_sprite(sprite_t * sprite) {
  148. gfx_context_t * out = malloc(sizeof(gfx_context_t));
  149. out->clips = NULL;
  150. out->width = sprite->width;
  151. out->stride = sprite->width * sizeof(uint32_t);
  152. out->height = sprite->height;
  153. out->depth = 32;
  154. out->size = GFX_H(out) * GFX_W(out) * GFX_B(out);
  155. out->buffer = (char *)sprite->bitmap;
  156. out->backbuffer = out->buffer;
  157. return out;
  158. }
  159. sprite_t * create_sprite(size_t width, size_t height, int alpha) {
  160. sprite_t * out = malloc(sizeof(sprite_t));
  161. /*
  162. uint16_t width;
  163. uint16_t height;
  164. uint32_t * bitmap;
  165. uint32_t * masks;
  166. uint32_t blank;
  167. uint8_t alpha;
  168. */
  169. out->width = width;
  170. out->height = height;
  171. out->bitmap = malloc(sizeof(uint32_t) * out->width * out->height);
  172. out->masks = NULL;
  173. out->blank = 0x00000000;
  174. out->alpha = alpha;
  175. return out;
  176. }
  177. void sprite_free(sprite_t * sprite) {
  178. if (sprite->masks) {
  179. free(sprite->masks);
  180. }
  181. free(sprite->bitmap);
  182. free(sprite);
  183. }
  184. inline uint32_t rgb(uint8_t r, uint8_t g, uint8_t b) {
  185. return 0xFF000000 | (r << 16) | (g << 8) | (b);
  186. }
  187. inline uint32_t rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
  188. return (a << 24U) | (r << 16) | (g << 8) | (b);
  189. }
  190. uint32_t alpha_blend(uint32_t bottom, uint32_t top, uint32_t mask) {
  191. uint8_t a = _RED(mask);
  192. uint8_t red = (_RED(bottom) * (255 - a) + _RED(top) * a) / 255;
  193. uint8_t gre = (_GRE(bottom) * (255 - a) + _GRE(top) * a) / 255;
  194. uint8_t blu = (_BLU(bottom) * (255 - a) + _BLU(top) * a) / 255;
  195. uint8_t alp = (int)a + (int)_ALP(bottom) > 255 ? 255 : a + _ALP(bottom);
  196. return rgba(red,gre,blu, alp);
  197. }
  198. #define DONT_USE_FLOAT_FOR_ALPHA 1
  199. uint32_t alpha_blend_rgba(uint32_t bottom, uint32_t top) {
  200. if (_ALP(bottom) == 0) return top;
  201. if (_ALP(top) == 255) return top;
  202. if (_ALP(top) == 0) return bottom;
  203. #if DONT_USE_FLOAT_FOR_ALPHA
  204. uint16_t a = _ALP(top);
  205. uint16_t c = 255 - a;
  206. uint16_t b = ((int)_ALP(bottom) * c) / 255;
  207. uint16_t alp = min16(a + b, 255);
  208. uint16_t red = min16((uint32_t)(_RED(bottom) * c + _RED(top) * 255) / 255, 255);
  209. uint16_t gre = min16((uint32_t)(_GRE(bottom) * c + _GRE(top) * 255) / 255, 255);
  210. uint16_t blu = min16((uint32_t)(_BLU(bottom) * c + _BLU(top) * 255) / 255, 255);
  211. return rgba(red,gre,blu,alp);
  212. #else
  213. double a = _ALP(top) / 255.0;
  214. double c = 1.0 - a;
  215. double b = (_ALP(bottom) / 255.0) * c;
  216. double alp = a + b; if (alp > 1.0) alp = 1.0;
  217. double red = (_RED(bottom) / 255.0) * c + (_RED(top) / 255.0); if (red > 1.0) red = 1.0;
  218. double gre = (_GRE(bottom) / 255.0) * c + (_GRE(top) / 255.0); if (gre > 1.0) gre = 1.0;
  219. double blu = (_BLU(bottom) / 255.0) * c + (_BLU(top) / 255.0); if (blu > 1.0) blu = 1.0;
  220. return rgba(red * 255, gre * 255, blu * 255, alp * 255);
  221. #endif
  222. }
  223. uint32_t premultiply(uint32_t color) {
  224. uint16_t a = _ALP(color);
  225. uint16_t r = _RED(color);
  226. uint16_t g = _GRE(color);
  227. uint16_t b = _BLU(color);
  228. r = r * a / 255;
  229. g = g * a / 255;
  230. b = b * a / 255;
  231. return rgba(r,g,b,a);
  232. }
  233. static int clamp(int a, int l, int h) {
  234. return a < l ? l : (a > h ? h : a);
  235. }
  236. static void _box_blur_horizontal(gfx_context_t * _src, int radius) {
  237. int w = _src->width;
  238. int h = _src->height;
  239. int half_radius = radius / 2;
  240. uint32_t * out_color = calloc(sizeof(uint32_t), w);
  241. for (int y = 0; y < h; y++) {
  242. int hits = 0;
  243. int r = 0;
  244. int g = 0;
  245. int b = 0;
  246. int a = 0;
  247. for (int x = -half_radius; x < w; x++) {
  248. int old_p = x - half_radius - 1;
  249. if (old_p >= 0)
  250. {
  251. uint32_t col = GFX(_src, clamp(old_p,0,w-1), y);
  252. if (col) {
  253. r -= _RED(col);
  254. g -= _GRE(col);
  255. b -= _BLU(col);
  256. a -= _ALP(col);
  257. }
  258. hits--;
  259. }
  260. int newPixel = x + half_radius;
  261. if (newPixel < w) {
  262. uint32_t col = GFX(_src, clamp(newPixel,0,w-1), y);
  263. if (col != 0) {
  264. r += _RED(col);
  265. g += _GRE(col);
  266. b += _BLU(col);
  267. a += _ALP(col);
  268. }
  269. hits++;
  270. }
  271. if (x >= 0 && x < w) {
  272. out_color[x] = rgba(r / hits, g / hits, b / hits, a / hits);
  273. }
  274. }
  275. if (!_is_in_clip(_src, y)) continue;
  276. for (int x = 0; x < w; x++) {
  277. GFX(_src,x,y) = out_color[x];
  278. }
  279. }
  280. free(out_color);
  281. }
  282. static void _box_blur_vertical(gfx_context_t * _src, int radius) {
  283. int w = _src->width;
  284. int h = _src->height;
  285. int half_radius = radius / 2;
  286. uint32_t * out_color = calloc(sizeof(uint32_t), h);
  287. for (int x = 0; x < w; x++) {
  288. int hits = 0;
  289. int r = 0;
  290. int g = 0;
  291. int b = 0;
  292. int a = 0;
  293. for (int y = -half_radius; y < h; y++) {
  294. int old_p = y - half_radius - 1;
  295. if (old_p >= 0) {
  296. uint32_t col = GFX(_src,x,clamp(old_p,0,h-1));
  297. if (col != 0) {
  298. r -= _RED(col);
  299. g -= _GRE(col);
  300. b -= _BLU(col);
  301. a -= _ALP(col);
  302. }
  303. hits--;
  304. }
  305. int newPixel = y + half_radius;
  306. if (newPixel < h) {
  307. uint32_t col = GFX(_src,x,clamp(newPixel,0,h-1));
  308. if (col != 0)
  309. {
  310. r += _RED(col);
  311. g += _GRE(col);
  312. b += _BLU(col);
  313. a += _ALP(col);
  314. }
  315. hits++;
  316. }
  317. if (y >= 0 && y < h) {
  318. out_color[y] = rgba(r / hits, g / hits, b / hits, a / hits);
  319. }
  320. }
  321. for (int y = 0; y < h; y++) {
  322. if (!_is_in_clip(_src, y)) continue;
  323. GFX(_src,x,y) = out_color[y];
  324. }
  325. }
  326. free(out_color);
  327. }
  328. void blur_context_box(gfx_context_t * _src, int radius) {
  329. _box_blur_horizontal(_src,radius);
  330. _box_blur_vertical(_src,radius);
  331. }
  332. int load_sprite(sprite_t * sprite, char * filename) {
  333. /* Open the requested binary */
  334. FILE * image = fopen(filename, "r");
  335. if (!image) return 1;
  336. size_t image_size= 0;
  337. fseek(image, 0, SEEK_END);
  338. image_size = ftell(image);
  339. fseek(image, 0, SEEK_SET);
  340. /* Alright, we have the length */
  341. char * bufferb = malloc(image_size);
  342. fread(bufferb, image_size, 1, image);
  343. if (bufferb[0] == 'B' && bufferb[1] == 'M') {
  344. /* Bitmaps */
  345. uint16_t x = 0; /* -> 212 */
  346. uint16_t y = 0; /* -> 68 */
  347. /* Get the width / height of the image */
  348. signed int *bufferi = (signed int *)((uintptr_t)bufferb + 2);
  349. uint32_t width = bufferi[4];
  350. uint32_t height = bufferi[5];
  351. uint16_t bpp = bufferi[6] / 0x10000;
  352. uint32_t row_width = (bpp * width + 31) / 32 * 4;
  353. /* Skip right to the important part */
  354. size_t i = bufferi[2];
  355. sprite->width = width;
  356. sprite->height = height;
  357. sprite->bitmap = malloc(sizeof(uint32_t) * width * height);
  358. sprite->masks = NULL;
  359. for (y = 0; y < height; ++y) {
  360. for (x = 0; x < width; ++x) {
  361. if (i > image_size) goto _cleanup_sprite;
  362. /* Extract the color */
  363. uint32_t color;
  364. if (bpp == 24) {
  365. color = (bufferb[i + 3 * x] & 0xFF) +
  366. (bufferb[i+1 + 3 * x] & 0xFF) * 0x100 +
  367. (bufferb[i+2 + 3 * x] & 0xFF) * 0x10000 + 0xFF000000;
  368. } else if (bpp == 32) {
  369. if (bufferb[i + 4 * x] == 0) {
  370. color = 0x000000;
  371. } else {
  372. color = (bufferb[i + 4 * x] & 0xFF) * 0x1000000 +
  373. (bufferb[i+1 + 4 * x] & 0xFF) * 0x1 +
  374. (bufferb[i+2 + 4 * x] & 0xFF) * 0x100 +
  375. (bufferb[i+3 + 4 * x] & 0xFF) * 0x10000;
  376. color = premultiply(color);
  377. }
  378. } else {
  379. color = rgb(bufferb[i + x],bufferb[i + x],bufferb[i + x]); /* Unsupported */
  380. }
  381. /* Set our point */
  382. sprite->bitmap[(height - y - 1) * width + x] = color;
  383. }
  384. i += row_width;
  385. }
  386. } else {
  387. /* Assume targa; limited support */
  388. struct Header {
  389. uint8_t id_length;
  390. uint8_t color_map_type;
  391. uint8_t image_type;
  392. uint16_t color_map_first_entry;
  393. uint16_t color_map_length;
  394. uint8_t color_map_entry_size;
  395. uint16_t x_origin;
  396. uint16_t y_origin;
  397. uint16_t width;
  398. uint16_t height;
  399. uint8_t depth;
  400. uint8_t descriptor;
  401. } __attribute__((packed));
  402. struct Header * header = (struct Header *)bufferb;
  403. if (header->id_length || header->color_map_type || (header->image_type != 2)) {
  404. /* Unable to parse */
  405. goto _cleanup_sprite;
  406. }
  407. sprite->width = header->width;
  408. sprite->height = header->height;
  409. sprite->bitmap = malloc(sizeof(uint32_t) * sprite->width * sprite->height);
  410. sprite->masks = NULL;
  411. uint16_t x = 0;
  412. uint16_t y = 0;
  413. int i = sizeof(struct Header);
  414. if (header->depth == 24) {
  415. for (y = 0; y < sprite->height; ++y) {
  416. for (x = 0; x < sprite->width; ++x) {
  417. uint32_t color = rgb(
  418. bufferb[i+2 + 3 * x],
  419. bufferb[i+1 + 3 * x],
  420. bufferb[i + 3 * x]);
  421. sprite->bitmap[(sprite->height - y - 1) * sprite->width + x] = color;
  422. }
  423. i += sprite->width * 3;
  424. }
  425. } else if (header->depth == 32) {
  426. for (y = 0; y < sprite->height; ++y) {
  427. for (x = 0; x < sprite->width; ++x) {
  428. uint32_t color = rgba(
  429. bufferb[i+2 + 4 * x],
  430. bufferb[i+1 + 4 * x],
  431. bufferb[i + 4 * x],
  432. bufferb[i+3 + 4 * x]);
  433. sprite->bitmap[(sprite->height - y - 1) * sprite->width + x] = color;
  434. }
  435. i += sprite->width * 4;
  436. }
  437. }
  438. }
  439. _cleanup_sprite:
  440. fclose(image);
  441. free(bufferb);
  442. return 0;
  443. }
  444. #ifndef NO_SSE
  445. static __m128i mask00ff;
  446. static __m128i mask0080;
  447. static __m128i mask0101;
  448. __attribute__((constructor)) static void _masks(void) {
  449. mask00ff = _mm_set1_epi16(0x00FF);
  450. mask0080 = _mm_set1_epi16(0x0080);
  451. mask0101 = _mm_set1_epi16(0x0101);
  452. }
  453. #endif
  454. __attribute__((__force_align_arg_pointer__))
  455. void draw_sprite(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y) {
  456. int32_t _left = max(x, 0);
  457. int32_t _top = max(y, 0);
  458. int32_t _right = min(x + sprite->width, ctx->width - 1);
  459. int32_t _bottom = min(y + sprite->height, ctx->height - 1);
  460. if (sprite->alpha == ALPHA_MASK) {
  461. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  462. if (!_is_in_clip(ctx, y + _y)) continue;
  463. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  464. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  465. continue;
  466. GFX(ctx, x + _x, y + _y) = alpha_blend(GFX(ctx, x + _x, y + _y), SPRITE(sprite, _x, _y), SMASKS(sprite, _x, _y));
  467. }
  468. }
  469. } else if (sprite->alpha == ALPHA_EMBEDDED) {
  470. /* Alpha embedded is the most important step. */
  471. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  472. if (!_is_in_clip(ctx, y + _y)) continue;
  473. #ifdef NO_SSE
  474. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  475. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  476. continue;
  477. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), SPRITE(sprite, _x, _y));
  478. }
  479. #else
  480. uint16_t _x = 0;
  481. /* Ensure alignment */
  482. for (; _x < sprite->width; ++_x) {
  483. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  484. continue;
  485. if (!((uintptr_t)&GFX(ctx, x + _x, y + _y) & 15))
  486. break;
  487. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), SPRITE(sprite, _x, _y));
  488. }
  489. for (; _x < sprite->width - 3; _x += 4) {
  490. if (x + _x < _left || y + _y < _top || y + _y > _bottom) {
  491. continue;
  492. }
  493. if (x + _x + 3 > _right)
  494. break;
  495. __m128i d = _mm_load_si128((void *)&GFX(ctx, x + _x, y + _y));
  496. __m128i s = _mm_loadu_si128((void *)&SPRITE(sprite, _x, _y));
  497. __m128i d_l, d_h;
  498. __m128i s_l, s_h;
  499. // unpack destination
  500. d_l = _mm_unpacklo_epi8(d, _mm_setzero_si128());
  501. d_h = _mm_unpackhi_epi8(d, _mm_setzero_si128());
  502. // unpack source
  503. s_l = _mm_unpacklo_epi8(s, _mm_setzero_si128());
  504. s_h = _mm_unpackhi_epi8(s, _mm_setzero_si128());
  505. __m128i a_l, a_h;
  506. __m128i t_l, t_h;
  507. // extract source alpha RGBA → AAAA
  508. a_l = _mm_shufflehi_epi16(_mm_shufflelo_epi16(s_l, _MM_SHUFFLE(3,3,3,3)), _MM_SHUFFLE(3,3,3,3));
  509. a_h = _mm_shufflehi_epi16(_mm_shufflelo_epi16(s_h, _MM_SHUFFLE(3,3,3,3)), _MM_SHUFFLE(3,3,3,3));
  510. // negate source alpha
  511. t_l = _mm_xor_si128(a_l, mask00ff);
  512. t_h = _mm_xor_si128(a_h, mask00ff);
  513. // apply source alpha to destination
  514. d_l = _mm_mulhi_epu16(_mm_adds_epu16(_mm_mullo_epi16(d_l,t_l),mask0080),mask0101);
  515. d_h = _mm_mulhi_epu16(_mm_adds_epu16(_mm_mullo_epi16(d_h,t_h),mask0080),mask0101);
  516. // combine source and destination
  517. d_l = _mm_adds_epu8(s_l,d_l);
  518. d_h = _mm_adds_epu8(s_h,d_h);
  519. // pack low + high and write back to memory
  520. _mm_storeu_si128((void*)&GFX(ctx, x + _x, y + _y), _mm_packus_epi16(d_l,d_h));
  521. }
  522. for (; _x < sprite->width; ++_x) {
  523. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  524. continue;
  525. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), SPRITE(sprite, _x, _y));
  526. }
  527. #endif
  528. }
  529. } else if (sprite->alpha == ALPHA_INDEXED) {
  530. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  531. if (!_is_in_clip(ctx, y + _y)) continue;
  532. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  533. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  534. continue;
  535. if (SPRITE(sprite, _x, _y) != sprite->blank) {
  536. GFX(ctx, x + _x, y + _y) = SPRITE(sprite, _x, _y) | 0xFF000000;
  537. }
  538. }
  539. }
  540. } else if (sprite->alpha == ALPHA_FORCE_SLOW_EMBEDDED) {
  541. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  542. if (!_is_in_clip(ctx, y + _y)) continue;
  543. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  544. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  545. continue;
  546. #if 1
  547. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), SPRITE(sprite, _x, _y));
  548. #else
  549. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(rgba(255,255,0,255), SPRITE(sprite, _x, _y));
  550. #endif
  551. }
  552. }
  553. } else {
  554. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  555. if (!_is_in_clip(ctx, y + _y)) continue;
  556. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  557. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  558. continue;
  559. GFX(ctx, x + _x, y + _y) = SPRITE(sprite, _x, _y) | 0xFF000000;
  560. }
  561. }
  562. }
  563. }
  564. void draw_line(gfx_context_t * ctx, int32_t x0, int32_t x1, int32_t y0, int32_t y1, uint32_t color) {
  565. int deltax = abs(x1 - x0);
  566. int deltay = abs(y1 - y0);
  567. int sx = (x0 < x1) ? 1 : -1;
  568. int sy = (y0 < y1) ? 1 : -1;
  569. int error = deltax - deltay;
  570. while (1) {
  571. if (x0 >= 0 && y0 >= 0 && x0 < ctx->width && y0 < ctx->height) {
  572. GFX(ctx, x0, y0) = color;
  573. }
  574. if (x0 == x1 && y0 == y1) break;
  575. int e2 = 2 * error;
  576. if (e2 > -deltay) {
  577. error -= deltay;
  578. x0 += sx;
  579. }
  580. if (e2 < deltax) {
  581. error += deltax;
  582. y0 += sy;
  583. }
  584. }
  585. }
  586. void draw_line_thick(gfx_context_t * ctx, int32_t x0, int32_t x1, int32_t y0, int32_t y1, uint32_t color, char thickness) {
  587. int deltax = abs(x1 - x0);
  588. int deltay = abs(y1 - y0);
  589. int sx = (x0 < x1) ? 1 : -1;
  590. int sy = (y0 < y1) ? 1 : -1;
  591. int error = deltax - deltay;
  592. while (1) {
  593. for (char j = -thickness; j <= thickness; ++j) {
  594. for (char i = -thickness; i <= thickness; ++i) {
  595. if (x0 + i >= 0 && x0 + i < ctx->width && y0 + j >= 0 && y0 + j < ctx->height) {
  596. GFX(ctx, x0 + i, y0 + j) = color;
  597. }
  598. }
  599. }
  600. if (x0 == x1 && y0 == y1) break;
  601. int e2 = 2 * error;
  602. if (e2 > -deltay) {
  603. error -= deltay;
  604. x0 += sx;
  605. }
  606. if (e2 < deltax) {
  607. error += deltax;
  608. y0 += sy;
  609. }
  610. }
  611. }
  612. void draw_fill(gfx_context_t * ctx, uint32_t color) {
  613. for (uint16_t y = 0; y < ctx->height; ++y) {
  614. for (uint16_t x = 0; x < ctx->width; ++x) {
  615. GFX(ctx, x, y) = color;
  616. }
  617. }
  618. }
  619. /* Bilinear filtering from Wikipedia */
  620. uint32_t getBilinearFilteredPixelColor(sprite_t * tex, double u, double v) {
  621. u *= tex->width;
  622. v *= tex->height;
  623. int x = floor(u);
  624. int y = floor(v);
  625. if (x >= tex->width) return 0;
  626. if (y >= tex->height) return 0;
  627. if (x <= 0) return 0;
  628. if (y <= 0) return 0;
  629. double u_ratio = u - x;
  630. double v_ratio = v - y;
  631. double u_o = 1 - u_ratio;
  632. double v_o = 1 - v_ratio;
  633. int r_ALP = 255;
  634. if (tex->alpha == ALPHA_MASK) {
  635. if (x == tex->width - 1 || y == tex->height - 1) return (SPRITE(tex,x,y) | 0xFF000000) & (0xFFFFFF + ((uint32_t)_RED(SMASKS(tex,x,y)) << 24));
  636. r_ALP = (_RED(SMASKS(tex,x,y)) * u_o + _RED(SMASKS(tex,x+1,y)) * u_ratio) * v_o + (_RED(SMASKS(tex,x,y+1)) * u_o + _RED(SMASKS(tex,x+1,y+1)) * u_ratio) * v_ratio;
  637. } else if (tex->alpha == ALPHA_EMBEDDED) {
  638. if (x == tex->width - 1 || y == tex->height - 1) return (SPRITE(tex,x,y) | 0xFF000000) & (0xFFFFFF + ((uint32_t)_ALP(SPRITE(tex,x,y)) << 24));
  639. r_ALP = (_ALP(SPRITE(tex,x,y)) * u_o + _ALP(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_ALP(SPRITE(tex,x,y+1)) * u_o + _ALP(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio;
  640. }
  641. if (x == tex->width - 1 || y == tex->height - 1) return SPRITE(tex,x,y);
  642. int r_RED = (_RED(SPRITE(tex,x,y)) * u_o + _RED(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_RED(SPRITE(tex,x,y+1)) * u_o + _RED(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio;
  643. int r_BLU = (_BLU(SPRITE(tex,x,y)) * u_o + _BLU(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_BLU(SPRITE(tex,x,y+1)) * u_o + _BLU(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio;
  644. int r_GRE = (_GRE(SPRITE(tex,x,y)) * u_o + _GRE(SPRITE(tex,x+1,y)) * u_ratio) * v_o + (_GRE(SPRITE(tex,x,y+1)) * u_o + _GRE(SPRITE(tex,x+1,y+1)) * u_ratio) * v_ratio;
  645. return rgb(r_RED,r_GRE,r_BLU) & (0xFFFFFF + ((uint32_t)r_ALP << 24));
  646. }
  647. void draw_sprite_scaled(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y, uint16_t width, uint16_t height) {
  648. int32_t _left = max(x, 0);
  649. int32_t _top = max(y, 0);
  650. int32_t _right = min(x + width, ctx->width - 1);
  651. int32_t _bottom = min(y + height, ctx->height - 1);
  652. for (uint16_t _y = 0; _y < height; ++_y) {
  653. if (!_is_in_clip(ctx, y + _y)) continue;
  654. for (uint16_t _x = 0; _x < width; ++_x) {
  655. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  656. continue;
  657. if (sprite->alpha > 0) {
  658. uint32_t n_color = getBilinearFilteredPixelColor(sprite, (double)_x / (double)width, (double)_y/(double)height);
  659. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), n_color);
  660. } else {
  661. GFX(ctx, x + _x, y + _y) = getBilinearFilteredPixelColor(sprite, (double)_x / (double)width, (double)_y/(double)height);
  662. }
  663. }
  664. }
  665. }
  666. void draw_sprite_alpha(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y, float alpha) {
  667. int32_t _left = max(x, 0);
  668. int32_t _top = max(y, 0);
  669. int32_t _right = min(x + sprite->width, ctx->width - 1);
  670. int32_t _bottom = min(y + sprite->height, ctx->height - 1);
  671. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  672. if (!_is_in_clip(ctx, y + _y)) continue;
  673. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  674. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  675. continue;
  676. uint32_t n_color = SPRITE(sprite, _x, _y);
  677. uint32_t f_color = premultiply((n_color & 0xFFFFFF) | ((uint32_t)(255 * alpha) << 24));
  678. f_color = (f_color & 0xFFFFFF) | ((uint32_t)(alpha * _ALP(n_color)) << 24);
  679. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), f_color);
  680. }
  681. }
  682. }
  683. void draw_sprite_alpha_paint(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y, float alpha, uint32_t c) {
  684. int32_t _left = max(x, 0);
  685. int32_t _top = max(y, 0);
  686. int32_t _right = min(x + sprite->width, ctx->width - 1);
  687. int32_t _bottom = min(y + sprite->height, ctx->height - 1);
  688. for (uint16_t _y = 0; _y < sprite->height; ++_y) {
  689. if (!_is_in_clip(ctx, y + _y)) continue;
  690. for (uint16_t _x = 0; _x < sprite->width; ++_x) {
  691. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  692. continue;
  693. uint32_t n_color = SPRITE(sprite, _x, _y);
  694. uint32_t f_color = rgb(_ALP(n_color) * alpha, 0, 0);
  695. GFX(ctx, x + _x, y + _y) = alpha_blend(GFX(ctx, x + _x, y + _y), c, f_color);
  696. }
  697. }
  698. }
  699. void draw_sprite_scaled_alpha(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y, uint16_t width, uint16_t height, float alpha) {
  700. int32_t _left = max(x, 0);
  701. int32_t _top = max(y, 0);
  702. int32_t _right = min(x + width, ctx->width - 1);
  703. int32_t _bottom = min(y + height, ctx->height - 1);
  704. for (uint16_t _y = 0; _y < height; ++_y) {
  705. if (!_is_in_clip(ctx, y + _y)) continue;
  706. for (uint16_t _x = 0; _x < width; ++_x) {
  707. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  708. continue;
  709. uint32_t n_color = getBilinearFilteredPixelColor(sprite, (double)_x / (double)width, (double)_y/(double)height);
  710. uint32_t f_color = premultiply((n_color & 0xFFFFFF) | ((uint32_t)(255 * alpha) << 24));
  711. f_color = (f_color & 0xFFFFFF) | ((uint32_t)(alpha * _ALP(n_color)) << 24);
  712. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), f_color);
  713. }
  714. }
  715. }
  716. uint32_t interp_colors(uint32_t bottom, uint32_t top, uint8_t interp) {
  717. uint8_t red = (_RED(bottom) * (255 - interp) + _RED(top) * interp) / 255;
  718. uint8_t gre = (_GRE(bottom) * (255 - interp) + _GRE(top) * interp) / 255;
  719. uint8_t blu = (_BLU(bottom) * (255 - interp) + _BLU(top) * interp) / 255;
  720. uint8_t alp = (_ALP(bottom) * (255 - interp) + _ALP(top) * interp) / 255;
  721. return rgba(red,gre,blu, alp);
  722. }
  723. void draw_rectangle(gfx_context_t * ctx, int32_t x, int32_t y, uint16_t width, uint16_t height, uint32_t color) {
  724. int32_t _left = max(x, 0);
  725. int32_t _top = max(y, 0);
  726. int32_t _right = min(x + width, ctx->width - 1);
  727. int32_t _bottom = min(y + height, ctx->height - 1);
  728. for (uint16_t _y = 0; _y < height; ++_y) {
  729. if (!_is_in_clip(ctx, y + _y)) continue;
  730. for (uint16_t _x = 0; _x < width; ++_x) {
  731. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  732. continue;
  733. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), color);
  734. }
  735. }
  736. }
  737. void draw_rectangle_solid(gfx_context_t * ctx, int32_t x, int32_t y, uint16_t width, uint16_t height, uint32_t color) {
  738. int32_t _left = max(x, 0);
  739. int32_t _top = max(y, 0);
  740. int32_t _right = min(x + width, ctx->width - 1);
  741. int32_t _bottom = min(y + height, ctx->height - 1);
  742. for (uint16_t _y = 0; _y < height; ++_y) {
  743. if (!_is_in_clip(ctx, y + _y)) continue;
  744. for (uint16_t _x = 0; _x < width; ++_x) {
  745. if (x + _x < _left || x + _x > _right || y + _y < _top || y + _y > _bottom)
  746. continue;
  747. GFX(ctx, x + _x, y + _y) = color;
  748. }
  749. }
  750. }
  751. void draw_rounded_rectangle(gfx_context_t * ctx, int32_t x, int32_t y, uint16_t width, uint16_t height, int radius, uint32_t color) {
  752. /* Draw a rounded rectangle */
  753. if (radius > width / 2) {
  754. radius = width / 2;
  755. }
  756. if (radius > height / 2) {
  757. radius = height / 2;
  758. }
  759. uint32_t c = premultiply(color);
  760. for (int row = y; row < y + height; row++){
  761. for (int col = x; col < x + width; col++) {
  762. if ((col < x + radius || col > x + width - radius - 1) &&
  763. (row < y + radius || row > y + height - radius - 1)) {
  764. continue;
  765. }
  766. GFX(ctx, col, row) = alpha_blend_rgba(GFX(ctx, col, row), c);
  767. }
  768. }
  769. /* draw the actual rounding */
  770. for (int i = 0; i < radius; ++i) {
  771. long r2 = radius * radius;
  772. long i2 = i * i;
  773. long j2 = r2 - i2;
  774. double j_max = sqrt((double)j2);
  775. for (int j = 0; j <= (int)j_max; ++j) {
  776. int _x = x + width - radius + i;
  777. int _y = y + height - radius + j;
  778. int _z = y + radius - j - 1;
  779. uint32_t c = color;
  780. if (j == (int)j_max) {
  781. c = premultiply(rgba(_RED(c),_GRE(c),_BLU(c),(int)((double)_ALP(c) * (j_max - (double)j))));
  782. } else {
  783. c = premultiply(c);
  784. }
  785. GFX(ctx, _x, _y) = alpha_blend_rgba(GFX(ctx, _x, _y), c);
  786. GFX(ctx, _x, _z) = alpha_blend_rgba(GFX(ctx, _x, _z), c);
  787. _x = x + radius - i - 1;
  788. GFX(ctx, _x, _y) = alpha_blend_rgba(GFX(ctx, _x, _y), c);
  789. GFX(ctx, _x, _z) = alpha_blend_rgba(GFX(ctx, _x, _z), c);
  790. }
  791. }
  792. }
  793. void draw_rounded_rectangle_pattern(gfx_context_t * ctx, int32_t x, int32_t y, uint16_t width, uint16_t height, int radius, uint32_t (*pattern)(int32_t x, int32_t y, double alpha, void * extra), void * extra) {
  794. /* Draw a rounded rectangle */
  795. if (radius > width / 2) {
  796. radius = width / 2;
  797. }
  798. if (radius > height / 2) {
  799. radius = height / 2;
  800. }
  801. for (int row = y; row < y + height; row++){
  802. for (int col = x; col < x + width; col++) {
  803. if ((col < x + radius || col > x + width - radius - 1) &&
  804. (row < y + radius || row > y + height - radius - 1)) {
  805. continue;
  806. }
  807. GFX(ctx, col, row) = alpha_blend_rgba(GFX(ctx, col, row), pattern(col,row,1.0,extra));
  808. }
  809. }
  810. /* draw the actual rounding */
  811. for (int i = 0; i < radius; ++i) {
  812. long r2 = radius * radius;
  813. long i2 = i * i;
  814. long j2 = r2 - i2;
  815. double j_max = sqrt((double)j2);
  816. for (int j = 0; j <= (int)j_max; ++j) {
  817. int _x = x + width - radius + i;
  818. int _y = y + height - radius + j;
  819. int _z = y + radius - j - 1;
  820. double alpha = (j_max - (double)j);
  821. GFX(ctx, _x, _y) = alpha_blend_rgba(GFX(ctx, _x, _y), pattern(_x,_y,alpha,extra));
  822. GFX(ctx, _x, _z) = alpha_blend_rgba(GFX(ctx, _x, _z), pattern(_x,_z,alpha,extra));
  823. _x = x + radius - i - 1;
  824. GFX(ctx, _x, _y) = alpha_blend_rgba(GFX(ctx, _x, _y), pattern(_x,_y,alpha,extra));
  825. GFX(ctx, _x, _z) = alpha_blend_rgba(GFX(ctx, _x, _z), pattern(_x,_z,alpha,extra));
  826. }
  827. }
  828. }
  829. float gfx_point_distance(struct gfx_point * a, struct gfx_point * b) {
  830. return sqrt((a->x - b->x) * (a->x - b->x) + (a->y - b->y) * (a->y - b->y));
  831. }
  832. float gfx_point_distance_squared(struct gfx_point * a, struct gfx_point * b) {
  833. return (a->x - b->x) * (a->x - b->x) + (a->y - b->y) * (a->y - b->y);
  834. }
  835. float gfx_point_dot(struct gfx_point * a, struct gfx_point * b) {
  836. return (a->x * b->x) + (a->y * b->y);
  837. }
  838. struct gfx_point gfx_point_sub(struct gfx_point * a, struct gfx_point * b) {
  839. struct gfx_point p = {a->x - b->x, a->y - b->y};
  840. return p;
  841. }
  842. struct gfx_point gfx_point_add(struct gfx_point * a, struct gfx_point * b) {
  843. struct gfx_point p = {a->x + b->x, a->y + b->y};
  844. return p;
  845. }
  846. #define fmax(a,b) ((a) > (b) ? (a) : (b))
  847. #define fmin(a,b) ((a) < (b) ? (a) : (b))
  848. float gfx_line_distance(struct gfx_point * p, struct gfx_point * v, struct gfx_point * w) {
  849. float lengthlength = gfx_point_distance_squared(v,w);
  850. if (lengthlength == 0.0) return gfx_point_distance(p, v); /* point */
  851. struct gfx_point p_v = gfx_point_sub(p,v);
  852. struct gfx_point w_v = gfx_point_sub(w,v);
  853. float tmp = gfx_point_dot(&p_v,&w_v) / lengthlength;
  854. tmp = fmin(1.0,tmp);
  855. float t = fmax(0.0, tmp);
  856. w_v.x *= t;
  857. w_v.y *= t;
  858. struct gfx_point v_t = gfx_point_add(v, &w_v);
  859. return gfx_point_distance(p, &v_t);
  860. }
  861. /**
  862. * This is slow, but it works...
  863. *
  864. * Maybe acceptable for baked UI elements?
  865. */
  866. void draw_line_aa(gfx_context_t * ctx, int x_1, int x_2, int y_1, int y_2, uint32_t color, float thickness) {
  867. struct gfx_point v = {(float)x_1, (float)y_1};
  868. struct gfx_point w = {(float)x_2, (float)y_2};
  869. for (int y = 0; y < ctx->height; ++y) {
  870. for (int x = 0; x < ctx->width; ++x) {
  871. struct gfx_point p = {x,y};
  872. float d = gfx_line_distance(&p,&v,&w);
  873. if (d < thickness + 0.5) {
  874. if (d < thickness - 0.5) {
  875. GFX(ctx,x,y) = color;
  876. } else {
  877. uint32_t f_color = rgb(255 * (1.0 - (d - thickness + 0.5)), 0, 0);
  878. GFX(ctx,x,y) = alpha_blend(GFX(ctx,x,y), color, f_color);
  879. }
  880. }
  881. }
  882. }
  883. }
  884. static void calc_rotation(double x, double y, double px, double py, double s, double c, double * u, double * v) {
  885. /* Translate to pivot */
  886. x -= px;
  887. y -= py;
  888. *u = (x * c - y * s) + px;
  889. *v = (x * s + y * c) + py;
  890. }
  891. void draw_sprite_rotate(gfx_context_t * ctx, sprite_t * sprite, int32_t x, int32_t y, float rotation, float alpha) {
  892. double originx = (double)sprite->width / 2.0;
  893. double originy = (double)sprite->height / 2.0;
  894. /* Calculate corners */
  895. double ul_x, ul_y;
  896. double ll_x, ll_y;
  897. double ur_x, ur_y;
  898. double lr_x, lr_y;
  899. double _s = sin(rotation);
  900. double _c = cos(rotation);
  901. calc_rotation(-sprite->width/2, -sprite->height/2, 0, 0, _s, _c, &ul_x, &ul_y);
  902. calc_rotation(-sprite->width/2, sprite->height/2, 0, 0, _s, _c, &ll_x, &ll_y);
  903. calc_rotation(sprite->width/2, -sprite->height/2, 0, 0, _s, _c, &ur_x, &ur_y);
  904. calc_rotation(sprite->width/2, sprite->height/2, 0, 0, _s, _c, &lr_x, &lr_y);
  905. _s = sin(-rotation);
  906. _c = cos(-rotation);
  907. /* Calculate bounds */
  908. int32_t _left = min(min(ul_x, ll_x), min(ur_x, lr_x));
  909. int32_t _top = min(min(ul_y, ll_y), min(ur_y, lr_y));
  910. int32_t _right = max(max(ul_x, ll_x), max(ur_x, lr_x));
  911. int32_t _bottom = max(max(ul_y, ll_y), max(ur_y, lr_y));
  912. for (int32_t _y = _top; _y < _bottom; ++_y) {
  913. if (_y + y < 0) continue;
  914. if (_y + y >= ctx->height) break;
  915. if (!_is_in_clip(ctx, y + _y)) continue;
  916. for (int32_t _x = _left; _x < _right; ++_x) {
  917. if (_x + x < 0) continue;
  918. if (_x + x >= ctx->width) break;
  919. double u, v;
  920. calc_rotation(_x + originx, _y + originy, originx, originy, _s, _c, &u, &v);
  921. uint32_t n_color = getBilinearFilteredPixelColor(sprite, u / (double)sprite->width, v/(double)sprite->height);
  922. uint32_t f_color = premultiply((n_color & 0xFFFFFF) | ((uint32_t)(255 * alpha) << 24));
  923. f_color = (f_color & 0xFFFFFF) | ((uint32_t)(alpha * _ALP(n_color)) << 24);
  924. GFX(ctx, x + _x, y + _y) = alpha_blend_rgba(GFX(ctx, x + _x, y + _y), f_color);
  925. }
  926. }
  927. }
  928. uint32_t gfx_vertical_gradient_pattern(int32_t x, int32_t y, double alpha, void * extra) {
  929. struct gradient_definition * gradient = extra;
  930. int base_r = _RED(gradient->top), base_g = _GRE(gradient->top), base_b = _BLU(gradient->top);
  931. int last_r = _RED(gradient->bottom), last_g = _GRE(gradient->bottom), last_b = _BLU(gradient->bottom);
  932. double gradpoint = (double)(y - (gradient->y)) / (double)gradient->height;
  933. if (alpha > 1.0) alpha = 1.0;
  934. if (alpha < 0.0) alpha = 0.0;
  935. return premultiply(rgba(
  936. base_r * (1.0 - gradpoint) + last_r * (gradpoint),
  937. base_g * (1.0 - gradpoint) + last_g * (gradpoint),
  938. base_b * (1.0 - gradpoint) + last_b * (gradpoint),
  939. alpha * 255));
  940. }