graphics.c 28 KB

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