Compare commits

...

5 Commits

  1. 1
      TODO
  2. 12
      src/cache.c3
  3. 2
      src/ugui_button.c3
  4. 18
      src/ugui_data.c3
  5. 2
      src/ugui_div.c3
  6. 2
      src/ugui_impl.c3
  7. 4
      src/ugui_slider.c3
  8. 10
      test/test_bittype.c3
  9. 14
      test/test_custom_hash.c3
  10. 226
      test/ugui_font.c3

@ -4,3 +4,4 @@
[ ] Port font system from C to C3 (rewrite1)
[ ] Update ARCHITECTURE.md
[ ] Write a README.md
[ ] Use an arena allocator for cache

@ -18,7 +18,7 @@ import std::collections::bitset;
import std::collections::map;
def BitArr = bitset::BitSet(<SIZE>) @private;
def IdTable = map::Map(<Key, usz>) @private;
def IdTable = map::HashMap(<Key, usz>) @private;
def IdTableEntry = map::Entry(<Key, usz>) @private;
const usz CACHE_NCYCLES = (usz)(SIZE * 2.0/3.0);
@ -44,7 +44,7 @@ macro Cache.cycle(&cache) @private {
fn void! Cache.init(&cache)
{
cache.table = map::new(<Key, usz>)(SIZE);
cache.table.new_init(capacity: SIZE);
// FIXME: this shit is SLOW
foreach (idx, bit : cache.used) { cache.used[idx] = false; }
foreach (idx, bit : cache.present) { cache.present[idx] = false; }
@ -83,10 +83,10 @@ fn Value*! Cache.search(&cache, Key id)
/* If there is no free space left then just return the first position */
fn usz Cache.get_free_spot(&cache) @private
{
// FIXME: This shit is SLOW, especially when clz() exists
foreach (idx, bit: cache.present) {
if (bit == false) {
return idx;
const BITS = $typeof(cache.present.data[0]).sizeof*8;
foreach (idx, d: cache.present.data) {
if (d.clz() != BITS) {
return idx*BITS + BITS-d.clz();
}
}
return 0;

@ -5,7 +5,7 @@ import std::io;
// draw a button, return the events on that button
fn ElemEvents! Ctx.button(&ctx, String label, Rect size)
{
Id id = hash(label);
Id id = label.hash();
Elem *parent = ctx.get_parent()!;
Elem *c_elem = ctx.get_elem(id)!;

@ -93,7 +93,7 @@ struct Elem {
def IdTree = vtree::VTree(<Id>) @private;
// elements themselves are kept in a cache
const uint MAX_ELEMENTS = 2048;
const uint MAX_ELEMENTS = 1024;
def ElemCache = cache::Cache(<Id, Elem, MAX_ELEMENTS>) @private;
def CmdQueue = fifo::Fifo(<Cmd>);
@ -104,22 +104,6 @@ fault UgError {
UNEXPECTED_ELEMENT,
}
fn Id fnv1a(String str)
{
const ulong FNV_OFF = 0xcbf29ce484222325;
const ulong FNV_PRIME = 0x100000001b3;
ulong hash = FNV_OFF;
foreach (c : str) {
hash ^= c;
hash *= FNV_PRIME;
}
return hash;
}
macro hash(String str) { return fnv1a(str); }
macro uint_to_rgba(uint u) {
return Color{
.r = (char)((u >> 24) & 0xff),

@ -4,7 +4,7 @@ import std::io;
fn void! Ctx.div_begin(&ctx, String label, Rect size)
{
Id id = hash(label);
Id id = label.hash();
Elem *parent = ctx.get_parent()!;
Elem* c_elem = ctx.get_elem(id)!;

@ -28,7 +28,7 @@ macro Ctx.get_elem(&ctx, Id id)
// if it does't find one
macro Ctx.get_elem_by_label(&ctx, String label)
{
Id id = hash(label);
Id id = label.hash();
return ctx.cache.search(id);
}

@ -9,7 +9,7 @@ import std::io;
*/
fn ElemEvents! Ctx.slider_hor(&ctx, String label, Rect size)
{
Id id = hash(label);
Id id = label.hash();
Elem *parent = ctx.get_parent()!;
Elem *c_elem = ctx.get_elem(id)!;
@ -81,7 +81,7 @@ fn ElemEvents! Ctx.slider_hor(&ctx, String label, Rect size)
*/
fn ElemEvents! Ctx.slider_ver(&ctx, String label, Rect size)
{
Id id = hash(label);
Id id = label.hash();
Elem *parent = ctx.get_parent()!;
Elem *c_elem = ctx.get_elem(id)!;

@ -0,0 +1,10 @@
import std::io;
import std::collections::bitset;
def Bits = bitset::BitSet(<128>);
fn void main()
{
Bits b;
io::printn($typeof(b.data[0]).sizeof);
}

@ -0,0 +1,14 @@
import std::collections::map;
def Codepoint = uint;
fn uint Codepoint.hash(Codepoint code) => code < 128 ? code : ((uint)code).hash();
def CodeMap = map::HashMap(<Codepoint, Codepoint>);
fn int main()
{
CodeMap m;
m.new_init();
m.free();
return 0;
}

@ -0,0 +1,226 @@
module ugui;
import cache;
//#include <grapheme.h>
//#include <assert.h>
//#include "stb_truetype.h"
//#include "stbimage_write.h"
// unicode code point, different type for a different hash
def Codepoint = uint;
/* width and height of a glyph contain the kering advance
* (u,v)
* +-------------*---+ -
* | ^ | | ^
* | |oy | | |
* | v | | |
* | .ii. | | |
* | @@@@@@. |<->| |
* | V@Mio@@o |adv| |h
* | :i. V@V | | |
* | :oM@@M | | |
* | :@@@MM@M | | |
* | @@o o@M | | |
* |<->:@@. M@M | | |
* |ox @@@o@@@@ | | |
* | :M@@V:@@.| | v
* +-------------*---+ -
* |<------------->|
* w
*/
struct Glyph {
Codepoint code;
uint u, v;
ushort w, h, a, x, y;
}
def GlyphCache = cache::Cache(<Codepoint, Glyph, 1024>);
// identity map the ASCII range
fn uint Codepoint.hash(Codepoint code) => code < 128 ? code : ((uint)code).hash();
struct FontAtlas {
uint width, height;
char* atlas;
uint glyph_max_w, glyph_max_h;
int size;
int file_size;
char *file;
void *priv;
}
macro is_utf8(char c) => c & 0x80;
const uint BDEPTH = 1;
const uint BORDER = 4;
// FIXME: as of now only monospaced fonts look decent since no
// kerning information is stored
struct Priv @private {
stbtt_fontinfo stb;
float scale;
int baseline;
unsigned char *bitmap;
struct cache c;
}
//#define PRIV(x) ((struct priv *)x->priv)
struct font_atlas * font_init(void)
{
struct font_atlas *p = emalloc(sizeof(struct font_atlas));
memset(p, 0, sizeof(struct font_atlas));
p->priv = emalloc(sizeof(struct priv));
memset(p->priv, 0, sizeof(struct priv));
PRIV(p)->c = cache_init();
return p;
}
// loads a font into memory, storing all the ASCII characters in the atlas, each font
// atlas structure holds glyphs of a specific size in pixels
// NOTE: size includes ascend and descend (so 12 does not mean that 'A' is 12px tall)
int font_load(struct font_atlas *atlas, const char *path, int size)
{
if (!atlas || !path)
return -1;
int err;
dump_file(path, &(atlas->file), &(atlas->file_size));
err = stbtt_InitFont(&(PRIV(atlas)->stb), (unsigned char *)atlas->file, 0);
if (err == 0) return -1;
int ascent, descent, linegap, baseline;
int x0,y0,x1,y1;
float scale;
stbtt_GetFontVMetrics(&(PRIV(atlas)->stb), &ascent, &descent, &linegap);
stbtt_GetFontBoundingBox(&(PRIV(atlas)->stb), &x0, &y0, &x1, &y1);
scale = stbtt_ScaleForPixelHeight(&(PRIV(atlas)->stb), size);
baseline = scale * -y0;
atlas->glyph_max_w = (scale*x1) - (scale*x0);
atlas->glyph_max_h = (baseline+scale*y1) - (baseline+scale*y0);
atlas->atlas = emalloc(CACHE_SIZE*BDEPTH*atlas->glyph_max_w*atlas->glyph_max_h);
memset(atlas->atlas, 0, CACHE_SIZE*BDEPTH*atlas->glyph_max_w*atlas->glyph_max_h);
PRIV(atlas)->baseline = atlas->glyph_max_h - baseline;
PRIV(atlas)->scale = scale;
PRIV(atlas)->bitmap = emalloc(BDEPTH*atlas->glyph_max_w*atlas->glyph_max_h);
// FIXME: make this a square atlas
atlas->width = atlas->glyph_max_w*CACHE_SIZE/4;
atlas->height = atlas->glyph_max_h*4;
atlas->size = size;
// preallocate all ascii characters
for (char c = ' '; c <= '~'; c++) {
if (!font_get_glyph_texture(atlas, c, NULL))
return -1;
}
return 0;
}
int font_free(struct font_atlas *atlas)
{
efree(atlas->atlas);
efree(atlas->file);
efree(PRIV(atlas)->bitmap);
cache_free(&PRIV(atlas)->c);
efree(atlas->priv);
efree(atlas);
return 0;
}
// TODO: time and take the median of the time it takes to generate the cache and
// the time it takes to draw the glyph
const struct font_glyph * font_get_glyph_texture(struct font_atlas *atlas, unsigned int code, int *updated)
{
int _u = 0;
if (!updated) updated = &_u;
const struct font_glyph *r;
if ((r = cache_search(&PRIV(atlas)->c, code)) != NULL) {
*updated = 0;
return r;
}
*updated = 1;
// generate the sdf and put it into the cache
// TODO: generate the whole block at once
int idx = stbtt_FindGlyphIndex(&PRIV(atlas)->stb, code);
int x0,y0,x1,y1,gw,gh,l,off_x,off_y,adv,base;
base = atlas->glyph_max_h - PRIV(atlas)->baseline;
stbtt_GetGlyphBitmapBoxSubpixel(
&PRIV(atlas)->stb,
idx,
PRIV(atlas)->scale,
PRIV(atlas)->scale,
0,0,
&x0,&y0,
&x1, &y1);
gw = x1 - x0;
gh = y1 - y0;
stbtt_GetGlyphHMetrics(&PRIV(atlas)->stb, idx, &adv, &l);
adv *= PRIV(atlas)->scale;
off_x = PRIV(atlas)->scale*l;
off_y = atlas->glyph_max_h+y0;
stbtt_MakeGlyphBitmapSubpixel(
&PRIV(atlas)->stb,
PRIV(atlas)->bitmap,
atlas->glyph_max_w,
atlas->glyph_max_h,
atlas->glyph_max_w,
PRIV(atlas)->scale,
PRIV(atlas)->scale,
0, 0,
idx);
// TODO: bounds check usign atlas height
// TODO: clear spot area in the atlas before writing on it
unsigned int spot = cache_get_free_spot(&PRIV(atlas)->c);
unsigned int ty = ((atlas->glyph_max_w * spot) / atlas->width) * atlas->glyph_max_h;
unsigned int tx = (atlas->glyph_max_w * spot) % atlas->width;
unsigned int w = atlas->width;
unsigned char *a = (void *)atlas->atlas;
//printf("max:%d %d spot:%d : %d %d %d %d\n", atlas->glyph_max_w, atlas->glyph_max_h, spot, tx, ty, off_x, off_y);
for (int y = 0; y < gh; y++) {
for (int x = 0; x < gw; x++) {
int c, r;
r = (ty+y)*w;
c = tx+x;
a[r+c] = PRIV(atlas)->bitmap[y*atlas->glyph_max_w+x];
}
}
struct font_glyph g = {
.codepoint = code,
.u = tx,
.v = ty,
.w = gw,
.h = gh,
.x = off_x,
.y = off_y-base,
.a = adv,
};
return cache_insert_at(&PRIV(atlas)->c, &g, g.codepoint, spot);
}
void font_dump(const struct font_atlas *atlas, const char *path)
{
stbi_write_png(
path,
atlas->width,
atlas->height,
BDEPTH,
atlas->atlas,
BDEPTH*atlas->width);
}
Loading…
Cancel
Save