123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- #include <stdio.h>
- #include <assert.h>
- #include <math.h>
- #include "lib/stb/stb_truetype.h"
- #include "xalloc.h"
- #include "renderer.h"
- #define MAX_GLYPHSET 256
- struct RenImage {
- RenColor *pixels;
- int width, height;
- };
- typedef struct {
- RenImage *image;
- stbtt_bakedchar glyphs[256];
- } GlyphSet;
- struct RenFont {
- void *data;
- stbtt_fontinfo stbfont;
- GlyphSet *sets[MAX_GLYPHSET];
- float size;
- int height;
- };
- static SDL_Window *window;
- static struct { int left, top, right, bottom; } clip;
- static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
- unsigned res, n;
- switch (*p & 0xf0) {
- case 0xf0 : res = *p & 0x07; n = 3; break;
- case 0xe0 : res = *p & 0x0f; n = 2; break;
- case 0xd0 :
- case 0xc0 : res = *p & 0x1f; n = 1; break;
- default : res = *p; n = 0; break;
- }
- while (n--) {
- res = (res << 6) | (*(++p) & 0x3f);
- }
- *dst = res;
- return p + 1;
- }
- void ren_init(SDL_Window *win) {
- assert(win);
- window = win;
- SDL_Surface *surf = SDL_GetWindowSurface(window);
- ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } );
- }
- void ren_update_rects(RenRect *rects, int count) {
- SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count);
- }
- void ren_set_clip_rect(RenRect rect) {
- clip.left = rect.x;
- clip.top = rect.y;
- clip.right = rect.x + rect.width;
- clip.bottom = rect.y + rect.height;
- }
- void ren_get_size(int *x, int *y) {
- SDL_Surface *surf = SDL_GetWindowSurface(window);
- *x = surf->w;
- *y = surf->h;
- }
- RenImage* ren_new_image(int width, int height) {
- assert(width > 0 && height > 0);
- RenImage *image = xmalloc(sizeof(RenImage) + width * height * sizeof(RenColor));
- image->pixels = (void*) (image + 1);
- image->width = width;
- image->height = height;
- return image;
- }
- void ren_free_image(RenImage *image) {
- xfree(image);
- }
- static GlyphSet* load_glyphset(RenFont *font, int idx) {
- GlyphSet *set = xcalloc(1, sizeof(GlyphSet));
- /* init image */
- int width = 128;
- int height = 128;
- retry:
- set->image = ren_new_image(width, height);
- /* load glyphs */
- float s =
- stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) /
- stbtt_ScaleForPixelHeight(&font->stbfont, 1);
- int res = stbtt_BakeFontBitmap(
- font->data, 0, font->size * s, (void*) set->image->pixels,
- width, height, idx * 256, 256, set->glyphs);
- /* retry with a larger image buffer if the buffer wasn't large enough */
- if (res < 0) {
- width *= 2;
- height *= 2;
- ren_free_image(set->image);
- goto retry;
- }
- /* adjust glyph yoffsets and xadvance */
- int ascent, descent, linegap;
- stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
- float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size);
- int scaled_ascent = ascent * scale + 0.5;
- for (int i = 0; i < 256; i++) {
- set->glyphs[i].yoff += scaled_ascent;
- set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance);
- }
- /* convert 8bit data to 32bit */
- for (int i = width * height - 1; i >= 0; i--) {
- uint8_t n = *((uint8_t*) set->image->pixels + i);
- set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n };
- }
- return set;
- }
- static GlyphSet* get_glyphset(RenFont *font, int codepoint) {
- int idx = (codepoint >> 8) % MAX_GLYPHSET;
- if (!font->sets[idx]) {
- font->sets[idx] = load_glyphset(font, idx);
- }
- return font->sets[idx];
- }
- RenFont* ren_load_font(const char *filename, float size) {
- RenFont *font = NULL;
- FILE *fp = NULL;
- /* init font */
- font = xcalloc(1, sizeof(RenFont));
- font->size = size;
- /* load font into buffer */
- fp = fopen(filename, "rb");
- if (!fp) { return NULL; }
- /* get size */
- fseek(fp, 0, SEEK_END); int buf_size = ftell(fp); fseek(fp, 0, SEEK_SET);
- /* load */
- font->data = xmalloc(buf_size);
- int _ = fread(font->data, 1, buf_size, fp); (void) _;
- fclose(fp);
- fp = NULL;
- /* init stbfont */
- int ok = stbtt_InitFont(&font->stbfont, font->data, 0);
- if (!ok) { goto fail; }
- /* get height and scale */
- int ascent, descent, linegap;
- stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap);
- float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size);
- font->height = (ascent - descent + linegap) * scale + 0.5;
- return font;
- fail:
- if (fp) { fclose(fp); }
- if (font) { xfree(font->data); }
- xfree(font);
- return NULL;
- }
- void ren_free_font(RenFont *font) {
- for (int i = 0; i < MAX_GLYPHSET; i++) {
- GlyphSet *set = font->sets[i];
- if (set) {
- ren_free_image(set->image);
- xfree(set);
- }
- }
- xfree(font->data);
- xfree(font);
- }
- void ren_set_font_tab_width(RenFont *font, int n) {
- GlyphSet *set = get_glyphset(font, '\t');
- set->glyphs['\t'].xadvance = n;
- }
- int ren_get_font_width(RenFont *font, const char *text) {
- int x = 0;
- const char *p = text;
- unsigned codepoint;
- while (*p) {
- p = utf8_to_codepoint(p, &codepoint);
- GlyphSet *set = get_glyphset(font, codepoint);
- stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
- x += g->xadvance;
- }
- return x;
- }
- int ren_get_font_height(RenFont *font) {
- return font->height;
- }
- static inline RenColor blend_pixel(RenColor dst, RenColor src) {
- int ia = 0xff - src.a;
- dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8;
- dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8;
- dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8;
- return dst;
- }
- static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) {
- src.a = (src.a * color.a) >> 8;
- int ia = 0xff - src.a;
- dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8);
- dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8);
- dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8);
- return dst;
- }
- #define rect_draw_loop(expr) \
- for (int j = y1; j < y2; j++) { \
- for (int i = x1; i < x2; i++) { \
- *d = expr; \
- d++; \
- } \
- d += dr; \
- }
- void ren_draw_rect(RenRect rect, RenColor color) {
- if (color.a == 0) { return; }
- int x1 = rect.x < clip.left ? clip.left : rect.x;
- int y1 = rect.y < clip.top ? clip.top : rect.y;
- int x2 = rect.x + rect.width;
- int y2 = rect.y + rect.height;
- x2 = x2 > clip.right ? clip.right : x2;
- y2 = y2 > clip.bottom ? clip.bottom : y2;
- SDL_Surface *surf = SDL_GetWindowSurface(window);
- RenColor *d = (RenColor*) surf->pixels;
- d += x1 + y1 * surf->w;
- int dr = surf->w - (x2 - x1);
- if (color.a == 0xff) {
- rect_draw_loop(color);
- } else {
- rect_draw_loop(blend_pixel(*d, color));
- }
- }
- void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) {
- if (color.a == 0) { return; }
- /* clip */
- int n;
- if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; }
- if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; }
- if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; }
- if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; }
- if (sub->width <= 0 || sub->height <= 0) {
- return;
- }
- /* draw */
- SDL_Surface *surf = SDL_GetWindowSurface(window);
- RenColor *s = image->pixels;
- RenColor *d = (RenColor*) surf->pixels;
- s += sub->x + sub->y * image->width;
- d += x + y * surf->w;
- int sr = image->width - sub->width;
- int dr = surf->w - sub->width;
- for (int j = 0; j < sub->height; j++) {
- for (int i = 0; i < sub->width; i++) {
- *d = blend_pixel2(*d, *s, color);
- d++;
- s++;
- }
- d += dr;
- s += sr;
- }
- }
- int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) {
- RenRect rect;
- const char *p = text;
- unsigned codepoint;
- while (*p) {
- p = utf8_to_codepoint(p, &codepoint);
- GlyphSet *set = get_glyphset(font, codepoint);
- stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
- rect.x = g->x0;
- rect.y = g->y0;
- rect.width = g->x1 - g->x0;
- rect.height = g->y1 - g->y0;
- ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color);
- x += g->xadvance;
- }
- return x;
- }
|