renderer.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #include <stdio.h>
  2. #include <assert.h>
  3. #include <math.h>
  4. #include "lib/stb/stb_truetype.h"
  5. #include "xalloc.h"
  6. #include "renderer.h"
  7. #define MAX_GLYPHSET 256
  8. struct RenImage {
  9. RenColor *pixels;
  10. int width, height;
  11. };
  12. typedef struct {
  13. RenImage *image;
  14. stbtt_bakedchar glyphs[256];
  15. } GlyphSet;
  16. struct RenFont {
  17. void *data;
  18. stbtt_fontinfo stbfont;
  19. GlyphSet *sets[MAX_GLYPHSET];
  20. float size;
  21. int height;
  22. };
  23. static SDL_Window *window;
  24. static struct { int left, top, right, bottom; } clip;
  25. static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
  26. unsigned res, n;
  27. switch (*p & 0xf0) {
  28. case 0xf0 : res = *p & 0x07; n = 3; break;
  29. case 0xe0 : res = *p & 0x0f; n = 2; break;
  30. case 0xd0 :
  31. case 0xc0 : res = *p & 0x1f; n = 1; break;
  32. default : res = *p; n = 0; break;
  33. }
  34. while (n--) {
  35. res = (res << 6) | (*(++p) & 0x3f);
  36. }
  37. *dst = res;
  38. return p + 1;
  39. }
  40. void ren_init(SDL_Window *win) {
  41. assert(win);
  42. window = win;
  43. SDL_Surface *surf = SDL_GetWindowSurface(window);
  44. ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } );
  45. }
  46. void ren_update_rects(RenRect *rects, int count) {
  47. SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count);
  48. }
  49. void ren_set_clip_rect(RenRect rect) {
  50. clip.left = rect.x;
  51. clip.top = rect.y;
  52. clip.right = rect.x + rect.width;
  53. clip.bottom = rect.y + rect.height;
  54. }
  55. void ren_get_size(int *x, int *y) {
  56. SDL_Surface *surf = SDL_GetWindowSurface(window);
  57. *x = surf->w;
  58. *y = surf->h;
  59. }
  60. RenImage* ren_new_image(int width, int height) {
  61. assert(width > 0 && height > 0);
  62. RenImage *image = xmalloc(sizeof(RenImage) + width * height * sizeof(RenColor));
  63. image->pixels = (void*) (image + 1);
  64. image->width = width;
  65. image->height = height;
  66. return image;
  67. }
  68. void ren_free_image(RenImage *image) {
  69. xfree(image);
  70. }
  71. static GlyphSet* load_glyphset(RenFont *font, int idx) {
  72. GlyphSet *set = xcalloc(1, sizeof(GlyphSet));
  73. /* init image */
  74. int width = 128;
  75. int height = 128;
  76. retry:
  77. set->image = ren_new_image(width, height);
  78. /* load glyphs */
  79. float s =
  80. stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) /
  81. stbtt_ScaleForPixelHeight(&font->stbfont, 1);
  82. int res = stbtt_BakeFontBitmap(
  83. font->data, 0, font->size * s, (void*) set->image->pixels,
  84. width, height, idx * 256, 256, set->glyphs);
  85. /* retry with a larger image buffer if the buffer wasn't large enough */
  86. if (res < 0) {
  87. width *= 2;
  88. height *= 2;
  89. ren_free_image(set->image);
  90. goto retry;
  91. }
  92. /* adjust glyph yoffsets and xadvance */
  93. int ascent, descent, linegap;
  94. stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
  95. float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size);
  96. int scaled_ascent = ascent * scale + 0.5;
  97. for (int i = 0; i < 256; i++) {
  98. set->glyphs[i].yoff += scaled_ascent;
  99. set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance);
  100. }
  101. /* convert 8bit data to 32bit */
  102. for (int i = width * height - 1; i >= 0; i--) {
  103. uint8_t n = *((uint8_t*) set->image->pixels + i);
  104. set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n };
  105. }
  106. return set;
  107. }
  108. static GlyphSet* get_glyphset(RenFont *font, int codepoint) {
  109. int idx = (codepoint >> 8) % MAX_GLYPHSET;
  110. if (!font->sets[idx]) {
  111. font->sets[idx] = load_glyphset(font, idx);
  112. }
  113. return font->sets[idx];
  114. }
  115. RenFont* ren_load_font(const char *filename, float size) {
  116. RenFont *font = NULL;
  117. FILE *fp = NULL;
  118. /* init font */
  119. font = xcalloc(1, sizeof(RenFont));
  120. font->size = size;
  121. /* load font into buffer */
  122. fp = fopen(filename, "rb");
  123. if (!fp) { return NULL; }
  124. /* get size */
  125. fseek(fp, 0, SEEK_END); int buf_size = ftell(fp); fseek(fp, 0, SEEK_SET);
  126. /* load */
  127. font->data = xmalloc(buf_size);
  128. int _ = fread(font->data, 1, buf_size, fp); (void) _;
  129. fclose(fp);
  130. fp = NULL;
  131. /* init stbfont */
  132. int ok = stbtt_InitFont(&font->stbfont, font->data, 0);
  133. if (!ok) { goto fail; }
  134. /* get height and scale */
  135. int ascent, descent, linegap;
  136. stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
  137. float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size);
  138. font->height = (ascent - descent + linegap) * scale + 0.5;
  139. return font;
  140. fail:
  141. if (fp) { fclose(fp); }
  142. if (font) { xfree(font->data); }
  143. xfree(font);
  144. return NULL;
  145. }
  146. void ren_free_font(RenFont *font) {
  147. for (int i = 0; i < MAX_GLYPHSET; i++) {
  148. GlyphSet *set = font->sets[i];
  149. if (set) {
  150. ren_free_image(set->image);
  151. xfree(set);
  152. }
  153. }
  154. xfree(font->data);
  155. xfree(font);
  156. }
  157. void ren_set_font_tab_width(RenFont *font, int n) {
  158. GlyphSet *set = get_glyphset(font, '\t');
  159. set->glyphs['\t'].xadvance = n;
  160. }
  161. int ren_get_font_width(RenFont *font, const char *text) {
  162. int x = 0;
  163. const char *p = text;
  164. unsigned codepoint;
  165. while (*p) {
  166. p = utf8_to_codepoint(p, &codepoint);
  167. GlyphSet *set = get_glyphset(font, codepoint);
  168. stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
  169. x += g->xadvance;
  170. }
  171. return x;
  172. }
  173. int ren_get_font_height(RenFont *font) {
  174. return font->height;
  175. }
  176. static inline RenColor blend_pixel(RenColor dst, RenColor src) {
  177. int ia = 0xff - src.a;
  178. dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8;
  179. dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8;
  180. dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8;
  181. return dst;
  182. }
  183. static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) {
  184. src.a = (src.a * color.a) >> 8;
  185. int ia = 0xff - src.a;
  186. dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8);
  187. dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8);
  188. dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8);
  189. return dst;
  190. }
  191. #define rect_draw_loop(expr) \
  192. for (int j = y1; j < y2; j++) { \
  193. for (int i = x1; i < x2; i++) { \
  194. *d = expr; \
  195. d++; \
  196. } \
  197. d += dr; \
  198. }
  199. void ren_draw_rect(RenRect rect, RenColor color) {
  200. if (color.a == 0) { return; }
  201. int x1 = rect.x < clip.left ? clip.left : rect.x;
  202. int y1 = rect.y < clip.top ? clip.top : rect.y;
  203. int x2 = rect.x + rect.width;
  204. int y2 = rect.y + rect.height;
  205. x2 = x2 > clip.right ? clip.right : x2;
  206. y2 = y2 > clip.bottom ? clip.bottom : y2;
  207. SDL_Surface *surf = SDL_GetWindowSurface(window);
  208. RenColor *d = (RenColor*) surf->pixels;
  209. d += x1 + y1 * surf->w;
  210. int dr = surf->w - (x2 - x1);
  211. if (color.a == 0xff) {
  212. rect_draw_loop(color);
  213. } else {
  214. rect_draw_loop(blend_pixel(*d, color));
  215. }
  216. }
  217. void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) {
  218. if (color.a == 0) { return; }
  219. /* clip */
  220. int n;
  221. if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; }
  222. if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; }
  223. if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; }
  224. if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; }
  225. if (sub->width <= 0 || sub->height <= 0) {
  226. return;
  227. }
  228. /* draw */
  229. SDL_Surface *surf = SDL_GetWindowSurface(window);
  230. RenColor *s = image->pixels;
  231. RenColor *d = (RenColor*) surf->pixels;
  232. s += sub->x + sub->y * image->width;
  233. d += x + y * surf->w;
  234. int sr = image->width - sub->width;
  235. int dr = surf->w - sub->width;
  236. for (int j = 0; j < sub->height; j++) {
  237. for (int i = 0; i < sub->width; i++) {
  238. *d = blend_pixel2(*d, *s, color);
  239. d++;
  240. s++;
  241. }
  242. d += dr;
  243. s += sr;
  244. }
  245. }
  246. int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) {
  247. RenRect rect;
  248. const char *p = text;
  249. unsigned codepoint;
  250. while (*p) {
  251. p = utf8_to_codepoint(p, &codepoint);
  252. GlyphSet *set = get_glyphset(font, codepoint);
  253. stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
  254. rect.x = g->x0;
  255. rect.y = g->y0;
  256. rect.width = g->x1 - g->x0;
  257. rect.height = g->y1 - g->y0;
  258. ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color);
  259. x += g->xadvance;
  260. }
  261. return x;
  262. }