Alessandro Mauri 2 years ago
parent a32b211d20
commit 5018ccbbb0
  1. 4
      RENDERER
  2. 106
      text_rendering/cache.c
  3. 10
      text_rendering/cache.h
  4. 66
      text_rendering/font.c
  5. 40
      text_rendering/font.h
  6. 76
      text_rendering/hash.c
  7. 21
      text_rendering/hash.h
  8. 35
      text_rendering/main.c
  9. 5077
      text_rendering/stb_truetype.h
  10. BIN
      text_rendering/test
  11. 98
      text_rendering/util.c
  12. 13
      text_rendering/util.h
  13. 3
      ugui.c

@ -0,0 +1,4 @@
Divide up the OpenGL renderer into three parts, one part that draws simple shapes,
another that only draws text and one that is responsible for drawing icons and
mapping sprites in general, this way all the texture atlases are separate and
everything is done within 3 draw calls

@ -0,0 +1,106 @@
#define _POSIX_C_SOURCE 200809l
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "hash.h"
#include "font.h"
#include "util.h"
static struct hm_ref *hash_table;
static struct font_glyph cache_array[CACHE_SIZE] = {0};
// bitmap size is aligned to word
#define _BSIZE ((CACHE_SIZE+0x3f)&(~0x3f))
static uint64_t bitmap[_BSIZE] = {0};
// bitmap operations
#define B_RESET() memset(bitmap, 0, _BSIZE*sizeof(uint64_t))
#define B_SET(x) bitmap[(x)/_BSIZE] |= 1<<((x)%_BSIZE)
#define B_TEST(x) (bitmap[(x)/_BSIZE]&(1<<((x)%_BSIZE)))
// reset the bitmap every n cycles
#define NCYCLES (CACHE_SIZE/2)
static int cycles = 0;
static inline void set_bit(unsigned int x)
{
// printf("cycles: %d, set: %d\n", cycles, x);
cycles = (cycles+1)%NCYCLES;
if (!cycles) B_RESET();
B_SET(x);
}
void cache_init(void)
{
hash_table = hm_create(CACHE_SIZE);
}
void cache_destroy(void)
{
hm_destroy(hash_table);
}
struct font_glyph * cache_get(unsigned int code)
{
struct hm_entry *r = hm_search(hash_table, code);
// miss
if (!r)
return NULL;
// hit
set_bit((struct font_glyph *)(r->data)-cache_array);
return (struct font_glyph *)(r->data);
}
int cache_insert(struct font_glyph *g)
{
struct font_glyph *spot = NULL;
uint32_t x = 0;
// find an open spot in the cache
// TODO: use __builtin_clz to speed this up
for (; x < CACHE_SIZE; x++) {
// printf("test: %d\n", x);
if (!B_TEST(x))
break;
}
// allocation in cache failed
if (B_TEST(x))
return -1;
set_bit(x);
spot = &cache_array[x];
*spot = *g;
/*
for (int i = 0; i < _BSIZE; i++) {
//print_byte(bitmap[i]);
//print_byte(bitmap[i]>>8);
//print_byte(bitmap[i]>>16);
//print_byte(bitmap[i]>>24);
//print_byte(bitmap[i]>>32);
//print_byte(bitmap[i]>>40);
//print_byte(bitmap[i]>>48);
//print_byte(bitmap[i]>>56);
printf("%lx", bitmap[i]);
}
printf("\n");
*/
struct hm_entry e = { .code = g->codepoint, .data = spot};
if (!hm_insert(hash_table, &e))
return -1;
return 0;
}

@ -0,0 +1,10 @@
#ifndef _CACHE_H
#define _CACHE_H
void cache_init(void);
void cache_destroy(void);
struct font_glyph * cache_get(unsigned int code);
int cache_insert(struct font_glyph *g);
#endif

@ -0,0 +1,66 @@
#define _POSIX_C_SOURCE 200809l
#define STB_TRUETYPE_IMPLEMENTATION
#define STBTT_STATIC
#include <grapheme.h>
#include "stb_truetype.h"
#include "util.h"
#include "font.h"
#define UTF8(c) (c&0x80)
// Generates a cached atlas of font glyphs encoded usign a signed distance field
// https://www.youtube.com/watch?v=1b5hIMqz_wM
// https://github.com/pjako/msdf_c
// this way the texture atlas for the font will be bigger but we save up the space
// needed for rendering the font in multiple sizes
struct font_atlas {
unsigned int glyphs, width, height;
unsigned char *atlas;
unsigned int glyph_max_w, glyph_max_h;
struct {
stbtt_fontinfo info;
float scale;
} stb;
int file_size;
unsigned char *file;
};
// loads a font into memory, storing all the ASCII characters in the atlas
int load_font(struct font_atlas *atlas, const char *path, int height)
{
if (!atlas || !path)
return -1;
dump_file(path, &(atlas->file), &(atlas->file_size));
stbtt_InitFont(&(atlas->stb.info), atlas->file, 0);
atlas->stb.scale = stbtt_ScaleForPixelHeight(&(atlas->stb.info), height);
int ascent, descent, linegap, baseline;
int x0,y0,x1,y1;
stbtt_GetFontVMetrics(&(atlas->stb.info), &ascent, &descent, &linegap);
stbtt_GetFontBoundingBox(&(atlas->stb.info), &x0, &y0, &x1, &y1);
baseline = atlas->stb.scale * -y0;
atlas->glyph_max_w = (atlas->stb.scale*x1) - (atlas->stb.scale*x0);
atlas->glyph_max_h = (baseline+atlas->stb.scale*y1) - (baseline+atlas->stb.scale*y0);
atlas->atlas = emalloc(atlas->glyph_max_w*atlas->glyph_max_h*CACHE_SIZE);
return 0;
}
int free_font(struct font_atlas *atlas)
{
efree(atlas->atlas);
efree(atlas->file);
return 0;
}

@ -0,0 +1,40 @@
#ifndef _FONT_H
#define _FONT_H
#define CACHE_SIZE 512
/* width and height of a glyph contain the kering advance
* (u,v)
* +----------*---+ -
* | .ii. | | ^
* | @@@@@@. |<->| |
* | V@Mio@@o |adv| |
* | :i. V@V | | |
* | :oM@@M | | |
* | :@@@MM@M | | |
* | @@o o@M | | |
* |:@@. M@M | | |
* | @@@o@@@@ | | |
* | :M@@V:@@.| | v
* +----------*---+ -
* |<------------->|
* w
*/
struct font_glyph {
unsigned int codepoint;
unsigned int u, v, w, h;
};
struct font_atlas;
int load_font(struct font_atlas *atlas, const char *path, int height);
int free_font(struct font_atlas *atlas);
void cache_init(void);
void cache_destroy(void);
struct font_glyph * cache_get(unsigned int code);
int cache_insert(struct font_glyph *g);
#endif

@ -0,0 +1,76 @@
#define _POSIX_C_SOURCE 200809l
#define _DEFAULT_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "hash.h"
#define MAXSIZE 4096
static unsigned int hash(unsigned int code)
{
// identity map the ascii range
if (code < 128) return code;
return (uint32_t)((uint64_t)(code*2654435761)>>32);
}
struct hm_ref * hm_create(unsigned int size)
{
if (!size || size > MAXSIZE)
return NULL;
// round to the greater power of two
size = 1<<__builtin_clz(size);
// FIXME: check for intger overflow here
struct hm_ref *h = malloc(sizeof(struct hm_ref)+sizeof(struct hm_entry)*size);
if (h) {
h->items = 0;
h->size = size;
memset(h->bucket, 0, sizeof(struct hm_ref)*size);
}
return h;
}
void hm_destroy(struct hm_ref *hm)
{
free(hm);
}
static struct hm_entry * lookup(struct hm_ref *hm, unsigned int code)
{
// fast modulo operation for power-of-2 size
unsigned int mask = hm->size - 1;
unsigned int i = hash(code);
for (unsigned int j = 1; ; i += j++) {
if (!hm->bucket[i&mask].code || hm->bucket[i].code == code)
return &(hm->bucket[i&mask]);
}
return NULL;
}
struct hm_entry * hm_search(struct hm_ref *hm, unsigned int code)
{
struct hm_entry *r = lookup(hm, code);
if (r) {
if (r->code == code)
return r;
return NULL;
}
return r;
}
struct hm_entry * hm_insert(struct hm_ref *hm, struct hm_entry *entry)
{
struct hm_entry *r = lookup(hm, entry->code);
if (r) *r = *entry;
return r;
}

@ -0,0 +1,21 @@
#ifndef _HASH_H
#define _HASH_H
struct hm_entry {
unsigned long code;
void *data;
};
struct hm_ref {
unsigned int items, size;
struct hm_entry bucket[];
};
struct hm_ref * hm_create(unsigned int size);
void hm_destroy(struct hm_ref *hm);
struct hm_entry * hm_search(struct hm_ref *hm, unsigned int code);
struct hm_entry * hm_insert(struct hm_ref *hm, struct hm_entry *entry);
#endif

@ -0,0 +1,35 @@
#include <stdlib.h>
#include <stdio.h>
#include <grapheme.h>
#include "cache.h"
#include "font.h"
int main(void)
{
cache_init();
struct font_glyph *g, b;
b.codepoint = 'a';
b.u = b.v = 10;
b.w = b.h = 20;
g = cache_get('a');
if (!g) printf("no element\n");
g = cache_get(0xa3c0);
if (!g) printf("not present\n");
const char *s = "κόσμε ciao mamma @à``²`²aas³³²";
size_t ret, off;
uint_least32_t cp;
for (off = 0; (ret = grapheme_decode_utf8(s+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) {
printf("%.*s (%d) -> %d\n", (int)ret, s+off, (int)ret, cp);
b.codepoint = cp;
if (cache_insert(&b)) printf("failed insert %d\n", b.codepoint);
if ((g = cache_get(cp))) printf("got %d\n", g->codepoint);
}
cache_destroy();
return 0;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -0,0 +1,98 @@
#define _POSIX_C_SOURCE 200809l
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include "util.h"
const char *bit_rep[16] = {
[ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
[ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
[ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
[12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
};
void * emalloc(unsigned long int size)
{
void *r = malloc(size);
if (!r)
err(EXIT_FAILURE, "malloc() of size %ld", size);
return r;
}
void * ecalloc(unsigned long int nmemb, unsigned long int size)
{
void *r = calloc(nmemb, size);
if (!r)
err(EXIT_FAILURE, "calloc() of size %ld, nmemb %ld", size, nmemb);
return r;
}
void efree(void *ptr)
{
if (ptr)
free(ptr);
}
void map_file(const unsigned char **str, int *size, const char *path)
{
if (!path) {
errno = EINVAL;
err(EXIT_FAILURE, "NULL filename");
}
FILE *fp = fopen(path, "r");
if (!fp)
err(EXIT_FAILURE, "Cannot open file %s", path);
*size = lseek(fileno(fp), 0, SEEK_END);
if (*size == (off_t)-1)
err(EXIT_FAILURE, "lseek() failed");
*str = mmap(0, *size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
if (*str == (void*)-1)
err(EXIT_FAILURE, "mmap() failed");
if (fclose(fp))
err(EXIT_FAILURE, "Error closing file");
}
void dump_file(const char *path, unsigned char **buf, int *buf_len)
{
if (!path) {
errno = EINVAL;
err(EXIT_FAILURE, "NULL filename");
}
if (!buf) {
errno = EINVAL;
err(EXIT_FAILURE, "No buffer specified");
}
if (!buf_len) {
errno = EINVAL;
err(EXIT_FAILURE, "Nowhere to store buffer size");
}
FILE *fp = fopen(path, "r");
if (!fp)
err(EXIT_FAILURE, "Cannot open file %s", path);
*buf_len = lseek(fileno(fp), 0, SEEK_END);
if (*buf_len == (off_t)-1)
err(EXIT_FAILURE, "lseek() failed");
*buf = emalloc(*buf_len);
*buf_len = fread(*buf, 1, *buf_len, fp);
if (fclose(fp))
err(EXIT_FAILURE, "Error closing file");
}
void print_byte(unsigned char byte)
{
printf("%s%s", bit_rep[byte >> 4], bit_rep[byte & 0x0F]);
}

@ -0,0 +1,13 @@
#ifndef _UTIL_H
#define _UTIL_H
void * emalloc(unsigned long int size);
void * ecalloc(unsigned long int nmemb, unsigned long int size);
void efree(void *ptr);
void map_file(const unsigned char **str, int *size, const char *path);
void dump_file(const char *path, unsigned char **buf, int *buf_len);
void print_byte(unsigned char byte);
#endif

@ -212,6 +212,9 @@ int crop_rect(ug_rect_t *ra, ug_rect_t *rb)
*=============================================================================*/
// TODO: when pushing onto the stack calculate the incremental hash of the whole
// stack instead of calculating it at the end, this saves us one last scroll
// trought the draw stack
static void push_rect_command(ug_ctx_t *ctx, const ug_rect_t *rect, ug_color_t color)
{
ug_cmd_t *c;

Loading…
Cancel
Save