reorganize files, port stbtt to d, remove truetype

This commit is contained in:
Matthew 2026-05-08 18:18:15 +10:00
parent f98e6cc07f
commit 2c81e42008
21 changed files with 6092 additions and 1161 deletions

View File

@ -7,13 +7,9 @@ import dlib.util;
import core.stdc.string; import core.stdc.string;
version(WebAssembly) static if(NativeTarget)
{ {
import dlib.externdecl; public import core.memory : malloc = pureMalloc, realloc = pureRealloc, free = pureFree;
}
else
{
import core.memory : malloc = pureMalloc, realloc = pureRealloc, free = pureFree;
} }
static Scratch g_scratch; static Scratch g_scratch;
@ -62,14 +58,14 @@ version(WebAssembly)
{ {
pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32") pragma(LDC_intrinsic, "llvm.wasm.memory.grow.i32")
extern(C) size_t grow(size_t index, size_t size); extern(C) usize grow(usize index, usize size) nothrow @nogc;
pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32") pragma(LDC_intrinsic, "llvm.wasm.memory.size.i32")
extern(C) size_t size(size_t index); extern(C) usize size(usize index) nothrow @nogc;
extern(C) u32 extern(C) u32
WasmGrow(u32 size) WasmGrow(u32 size) nothrow @nogc
{ {
return grow(0, size); return grow(0, size);
} }
@ -81,7 +77,7 @@ WasmGrow2(u32 index, u32 size)
} }
extern(C) u32 extern(C) u32
WasmSize() WasmSize() nothrow @nogc
{ {
return size(0); return size(0);
} }
@ -487,19 +483,457 @@ ScratchAlloc(T)(usize count, T set)
return arr; return arr;
} }
version(DLIB_TEST) unittest version(DLIB_TEST)
{
void WriteToArray(T)(T[] arr, T factor)
{
foreach(i; 0 .. arr.length)
{
arr[i] = i*factor;
}
}
void AssertArrayValues(T)(T[] arr, T factor)
{
foreach(i; 0 .. arr.length)
{
assert(arr[i] == i*factor);
}
}
void DLibTestAlloc()
{ {
{ {
u64[5] arr = [1, 2, 3, 4, 5]; u64[5] arr0 = [1, 2, 3, 4, 5];
u64[] copy = Alloc!(u64)(arr); u64[] copy = Alloc!(u64)(arr0);
assert(arr == copy); assert(arr0 == copy);
u64[] arr1 = Alloc!(u64)(64);
WriteToArray(arr1, 5);
assert(arr0 == copy);
AssertArrayValues(arr1, 5);
} }
{ {
Arena a = CreateArena(64); Arena a = CreateArena(64);
u64[] arr = Alloc!(u64)(&a, 128); u64[] arr0 = Alloc!(u64)(&a, 128);
u64[] arr1 = Alloc!(u64)(&a, 256);
WriteToArray(arr0, 2);
WriteToArray(arr1, 3);
WriteToArray(arr0, 2);
AssertArrayValues(arr0, 2);
AssertArrayValues(arr1, 3);
Reset(&a);
arr0 = Alloc!(u64)(&a, 256);
arr1 = Alloc!(u64)(&a, 128);
WriteToArray(arr0, 4);
WriteToArray(arr1, 5);
WriteToArray(arr0, 4);
AssertArrayValues(arr0, 4);
AssertArrayValues(arr1, 5);
} }
} }
unittest
{
DLibTestAlloc();
}
}
version(WebAssembly) nothrow @nogc:
struct MemBlock
{
void* addr;
MemBlock* next;
usize size;
}
struct Heap
{
MemBlock* free;
MemBlock* used;
MemBlock* fresh;
usize top;
}
extern extern(C) u8 __heap_base;
extern extern(C) u8 __data_end;
__gshared Heap* heap;
__gshared void* heap_limit;
__gshared usize heap_split_thresh;
__gshared usize heap_alignment;
__gshared usize heap_max_blocks;
__gshared usize current_pages;
enum bool MALLOC_COMPACT = true;
enum bool MALLOC_SPLIT = true;
usize WasmPageSize(usize x) => x*1024*64;
/**
* If compaction is enabled, inserts block
* into free list, sorted by addr.
* If disabled, add block has new head of
* the free list.
*/
void
InsertBlock(MemBlock *block)
{
static if(MALLOC_COMPACT)
{
MemBlock *ptr = heap.free;
MemBlock *prev = null;
while(ptr != null)
{
if(cast(usize)block.addr <= cast(usize)ptr.addr)
{
break;
}
prev = ptr;
ptr = ptr.next;
}
if(prev != null)
{
prev.next = block;
}
else
{
heap.free = block;
}
block.next = ptr;
}
else
{
block.next = heap.free;
heap.free = block;
}
}
void
ReleaseBlocks(MemBlock *scan, MemBlock *to)
{
MemBlock *scan_next;
while(scan != to)
{
scan_next = scan.next;
scan.next = heap.fresh;
heap.fresh = scan;
scan.addr = null;
scan.size = 0;
scan = scan_next;
}
}
void
Compact()
{
MemBlock *ptr = heap.free;
MemBlock *prev;
MemBlock *scan;
while(ptr != null)
{
prev = ptr;
scan = ptr.next;
while(scan != null && cast(usize)prev.addr + prev.size == cast(usize)scan.addr)
{
prev = scan;
scan = scan.next;
}
if(prev != ptr)
{
usize new_size = cast(usize)prev.addr - cast(usize)ptr.addr + prev.size;
ptr.size = new_size;
MemBlock *next = prev.next;
// make merged blocks available
ReleaseBlocks(ptr.next, prev.next);
// relink
ptr.next = next;
}
ptr = ptr.next;
}
}
void
MallocInit(const usize heap_blocks, const usize split_thresh, const usize alignment)
{
current_pages = WasmSize();
void* limit = cast(void *)WasmPageSize(current_pages);
heap = cast(Heap *)&__heap_base;
heap_limit = limit;
heap_split_thresh = split_thresh;
heap_alignment = alignment;
heap_max_blocks = heap_blocks;
heap.free = null;
heap.used = null;
heap.fresh = cast(MemBlock*)(heap + 1);
heap.top = cast(usize)(heap.fresh + heap_blocks);
MemBlock *block = heap.fresh;
usize i = heap_max_blocks - 1;
while(i--)
{
block.next = block + 1;
block++;
}
block.next = null;
}
void
free(void *ptr)
{
MemBlock *block = heap.used;
MemBlock *prev = null;
while(block != null)
{
if(ptr == block.addr)
{
if(prev)
{
prev.next = block.next;
}
else
{
heap.used = block.next;
}
InsertBlock(block);
static if(MALLOC_COMPACT)
{
Compact();
}
}
prev = block;
block = block.next;
}
}
usize
PtrInfo(void *ptr)
{
usize result = 0;
MemBlock *block = heap.used;
while(block != null)
{
if(ptr == block.addr)
{
result = block.size;
break;
}
}
return result;
}
MemBlock*
AllocBlock(usize num)
{
MemBlock *ptr = heap.free;
MemBlock *prev = null;
usize top = heap.top;
num = (num + heap_alignment - 1) & -heap_alignment;
while(ptr != null)
{
const i32 is_top = (cast(usize)ptr.addr + ptr.size >= top) && (cast(usize)ptr.addr + num <= cast(usize)heap_limit);
if(is_top || ptr.size >= num)
{
if(prev != null)
{
prev.next = ptr.next;
}
else
{
heap.free = ptr.next;
}
ptr.next = heap.used;
heap.used = ptr;
static if(!MALLOC_SPLIT)
{
if(is_top)
{
ptr.size = num;
heap.top = cast(usize)ptr.addr + num;
static if(MALLOC_COMPACT)
{
Compact();
}
}
}
else
{
if(is_top)
{
ptr.size = num;
heap.top = cast(usize)ptr.addr + num;
}
else if(heap.fresh != null)
{
usize excess = ptr.size - num;
if(excess >= heap_split_thresh)
{
ptr.size = num;
MemBlock *split = heap.fresh;
heap.fresh = split.next;
split.addr = cast(void *)(cast(usize)ptr.addr + num);
split.size = excess;
InsertBlock(split);
static if(MALLOC_COMPACT)
{
Compact();
}
}
}
}
return ptr;
}
prev = ptr;
ptr = ptr.next;
}
// no matching free blocks
// see if any other blocks available
usize new_top = top + num;
if(heap.fresh != null && new_top <= cast(usize)heap_limit)
{
ptr = heap.fresh;
heap.fresh = ptr.next;
ptr.addr = cast(void *)top;
ptr.next = heap.used;
ptr.size = num;
heap.used = ptr;
heap.top = new_top;
return ptr;
}
return null;
}
usize
BytesToPages(usize count)
{
usize pages = count/(1024*64);
return pages > 0 ? pages : 1;
}
void*
malloc(usize num)
{
MemBlock *block = AllocBlock(num);
if(block == null)
{
WasmGrow(BytesToPages(num));
usize new_pages = WasmSize();
usize page_diff = new_pages - current_pages;
if(page_diff > 0)
{
current_pages = new_pages;
heap_limit = cast(void*)WasmPageSize(current_pages);
block = AllocBlock(num);
}
}
return block != null ? block.addr : null;
}
void
MemClear(void *ptr, usize num)
{
usize *ptrw = cast(usize*)ptr;
usize numw = (num & -usize.sizeof) / usize.sizeof;
while(numw--)
{
*ptrw++ = 0;
}
num &= (usize.sizeof - 1);
u8* ptrb = cast(u8*)ptrw;
while(num--)
{
*ptrb++ = 0;
}
}
void*
calloc(usize num, usize size)
{
num *= size;
MemBlock *block = AllocBlock(num);
{
MemClear(block.addr, num);
return block.addr;
}
return null;
}
void*
realloc(void *ptr, usize size)
{
void *new_ptr = malloc(size);
usize prev_size = PtrInfo(ptr);
memcpy(new_ptr, ptr, prev_size);
free(ptr);
return new_ptr;
}
usize
CountBlocks(MemBlock *ptr)
{
usize num = 0;
while(ptr != null)
{
num++;
ptr = ptr.next;
}
return num;
}
usize MallocFreeCount()
{
return CountBlocks(heap.free);
}
usize MallocUsedCount()
{
return CountBlocks(heap.used);
}
usize MallocMallocFreshCount()
{
return CountBlocks(heap.fresh);
}
bool MallocCheck()
{
return heap_max_blocks == MallocFreeCount() + MallocUsedCount() + MallocMallocFreshCount();
}

View File

@ -27,7 +27,7 @@
#endif #endif
#ifndef NO_STBI #ifndef NO_STBI
# include "../external/stb/stb_truetype.h" // # include "../external/stb/stb_truetype.h"
#endif #endif
#ifdef DLIB_INCLUDE_VULKAN #ifdef DLIB_INCLUDE_VULKAN

View File

@ -2,53 +2,12 @@ module dlib.externdecl;
import dlib.aliases; import dlib.aliases;
enum stbtt_curvetype : u8 import stb_truetype;
{
none,
vmove,
vline,
vcurve,
vcubic,
}
alias stbtt_vertex_type = short;
struct stbtt__buf
{
u8* data;
i32 cursor;
i32 size;
}
struct stbtt_vertex
{
stbtt_vertex_type x, y, cx, cy, cx1, cy1;
stbtt_curvetype type;
u8 padding;
}
struct stbtt_fontinfo
{
void* userdata;
u8* data;
i32 fontstart;
i32 numGlyphs;
i32 loca, head, glyf, hhea, hmtx, kern, gpos, svg;
i32 index_map;
i32 indexToLocFormat;
stbtt__buf cff;
stbtt__buf charstrings;
stbtt__buf gsubrs;
stbtt__buf subrs;
stbtt__buf fontdicts;
stbtt__buf fdselect;
}
@nogc extern(C): @nogc extern(C):
/*
static if(NativeTarget):
// stb_truetype // stb_truetype
i32 i32
@ -78,24 +37,4 @@ stbtt_GetGlyphBox(const stbtt_fontinfo* info, i32 glyph_index, i32* x0, i32* y0,
i32 i32
stbtt_GetFontOffsetForIndex(const(u8*) data, i32 index); stbtt_GetFontOffsetForIndex(const(u8*) data, i32 index);
// tinyalloc */
version(WebAssembly)
{
void
ta_init(const usize heap_blocks, const usize split_thresh, const usize alignment);
void*
malloc(usize size) nothrow @nogc;
void*
calloc(usize size, usize count);
void
free(void *ptr);
void*
realloc(void *ptr, usize size);
}

View File

@ -1,18 +1,19 @@
module dlib.fonts; module dlib.fonts;
static if(NativeTarget)
{
import dlibincludes : FT_Library, FT_Face, FT_Error, FT_Bitmap, FT_GlyphSlot, FT_Int32, FT_ULong, FT_Long,
FT_Set_Pixel_Sizes, FT_Load_Char, FT_Done_Face, FT_New_Memory_Face, FT_Done_FreeType, FT_Init_FreeType,
FT_LOAD_RENDER, FT_PIXEL_MODE_GRAY, FT_PIXEL_MODE_MONO, FT_PIXEL_MODE_BGRA;
}
import dlib.externdecl; import dlib.externdecl;
import dlib.aliases; import dlib.aliases;
import dlib.util; import dlib.util;
import dlib.alloc; import dlib.alloc;
import dlib.math; import dlib.math;
import core.math : fabs;
import std.traits;
import stb_truetype;
alias FontFace = stbtt_fontinfo;
const u32 FONT_MAX_BANDS = 8; const u32 FONT_MAX_BANDS = 8;
const u32 FONT_TEX_WIDTH = 4096; const u32 FONT_TEX_WIDTH = 4096;
@ -141,68 +142,196 @@ struct SlugBuffer
struct FontAtlas struct FontAtlas
{ {
Glyph[128] glyphs; Glyph[128] glyphs;
u8[] data; f32 ascent = 0.0; // All unscaled
u32 size; f32 descent = 0.0;
UVec2 dimensions; f32 line_gap = 0.0;
f32 ascent; f32 line_height = 0.0;
f32 descent; f32 max_advance = 0.0;
f32 line_gap; }
f32 line_height;
f32 max_advance; struct GlyphBounds
{
f32 left = 0.0, bottom = 0.0, right = 0.0, top = 0.0;
} }
struct Glyph struct Glyph
{ {
dchar ch; dchar ch;
f32 advance; f32 advance;
f32 plane_left; GlyphBounds plane;
f32 plane_bottom; GlyphBounds atlas;
f32 plane_right;
f32 plane_top;
f32 atlas_left;
f32 atlas_bottom;
f32 atlas_right;
f32 atlas_top;
} }
static if(NativeTarget) enum MSDFType
{ {
None,
__gshared FT_Library FT_LIB; HardMask,
alias FontFace = FT_Face; SoftMask,
SDF,
void PSDF,
CloseFreeType() MSDF,
{ MTSDF,
if(FT_LIB)
{
FT_Done_FreeType(FT_LIB);
} }
enum MSDFYOrigin
{
None,
Left,
Bottom,
Right,
Top,
}
struct MSDFInfo
{
MSDFGlyph[] glyphs;
MSDFType type;
Vec2 aem_range;
u32 distance_field_range;
u32 distance_field_range_middle;
f32 pixels_per_em;
u32 texture_width;
u32 texture_height;
MSDFYOrigin y_origin;
f32 line_height; // EM
f32 ascender; // EM
f32 descender; // EM
f32 underline_y; // EM
f32 underline_thickness; // EM
}
struct MSDFGlyph
{
dchar glyph;
f32 advance = 0.0;
GlyphBounds plane; // EM
GlyphBounds atlas; // Pixels
}
static MSDFInfo
GenerateMSDF(string file_path)()
{
import std.json;
string msdf_json = import(file_path);
JSONValue jsonv = parseJSON(msdf_json);
static T GetJSONValue(T)(JSONValue jsonv, string name)
{
T result = Select!(is(T == string), null, cast(T)0);
if(const(JSONValue)* value = name in jsonv)
{
switch(value.type())
{
static if(is(T == u32) || is(T == u64) || is(T == f32))
{
case JSONType.float_: result = cast(T)value.floating; break;
case JSONType.uinteger: result = cast(T)value.uinteger; break;
case JSONType.integer: result = cast(T)value.integer; break;
}
static if(is(T == string))
{
case JSONType.string: result = cast(T)value.str; break;
}
default: assert(false, "Unable to retrieve value");
}
}
return result;
}
MSDFInfo result = {
distance_field_range: GetJSONValue!(u32)(jsonv["atlas"], "distanceRange"),
distance_field_range_middle: GetJSONValue!(u32)(jsonv["atlas"], "distanceRangeMiddle"),
pixels_per_em: GetJSONValue!(f32)(jsonv["atlas"], "size"),
texture_width: GetJSONValue!(u32)(jsonv["atlas"], "width"),
texture_height: GetJSONValue!(u32)(jsonv["atlas"], "height"),
line_height: GetJSONValue!(f32)(jsonv["metrics"], "lineHeight"),
ascender: GetJSONValue!(f32)(jsonv["metrics"], "ascender"),
descender: GetJSONValue!(f32)(jsonv["metrics"], "descender"),
underline_y: GetJSONValue!(f32)(jsonv["metrics"], "underlineY"),
underline_thickness: GetJSONValue!(f32)(jsonv["metrics"], "underlineThickness"),
};
result.aem_range = Vec2(
(result.distance_field_range_middle - result.distance_field_range/2) / result.pixels_per_em,
(result.distance_field_range_middle + result.distance_field_range/2) / result.pixels_per_em
);
string type_string = GetJSONValue!(string)(jsonv["atlas"], "type");
string y_origin_string = GetJSONValue!(string)(jsonv["atlas"], "yOrigin");
assert(type_string.length);
assert(y_origin_string.length);
final switch(type_string) with(MSDFType)
{
case "hardmask": result.type = HardMask; break;
case "softmask": result.type = SoftMask; break;
case "sdf": result.type = SDF; break;
case "psdf": result.type = PSDF; break;
case "msdf": result.type = MSDF; break;
case "mtsdf": result.type = MTSDF; break;
}
final switch(y_origin_string) with(MSDFYOrigin)
{
case "top": result.y_origin = Top; break;
case "left": result.y_origin = Left; break;
case "right": result.y_origin = Right; break;
case "bottom": result.y_origin = Bottom; break;
}
MSDFGlyph[] glyphs = new MSDFGlyph[128];
JSONValue[] glyph_json = jsonv["glyphs"].array;
foreach(i; 0 .. glyph_json.length)
{
u64 ch = cast(u64)glyph_json[i]["unicode"].integer;
MSDFGlyph glyph = { glyph: cast(dchar)ch };
glyph.advance = GetJSONValue!(f32)(glyph_json[i], "advance");
if("planeBounds" in glyph_json[i])
{
glyph.plane.left = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "left");
glyph.plane.right = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "right");
glyph.plane.bottom = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "bottom");
glyph.plane.top = GetJSONValue!(f32)(glyph_json[i]["planeBounds"], "top");
glyph.atlas.left = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "left");
glyph.atlas.right = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "right");
glyph.atlas.bottom = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "bottom");
glyph.atlas.top = GetJSONValue!(f32)(glyph_json[i]["atlasBounds"], "top");
}
glyphs[ch] = glyph;
}
foreach(i; 0 .. glyphs.length)
{
if(glyphs[i].glyph == 0xFFFF)
{
glyphs[i].glyph = 0;
glyphs[i].plane = GlyphBounds(left: 0.0, right: 0.0, top: 0.0, bottom: 0.0);
glyphs[i].atlas = GlyphBounds(left: 0.0, right: 0.0, top: 0.0, bottom: 0.0);
}
}
result.glyphs = glyphs;
return result;
} }
FontFace FontFace
OpenFont(u8[] data) OpenFont(u8[] data)
{ {
FontFace font; FontFace font;
FT_New_Memory_Face(FT_LIB, data.ptr, cast(FT_Long)data.length, 0, &font); stbtt_InitFont(&font, data.ptr, stbtt_GetFontOffsetForIndex(data.ptr, 0));
return font; return font;
} }
void
CloseFont(FontFace font)
{
if(font != null)
{
FT_Done_Face(font);
}
}
i32
Float26(f32 f)
{
return cast(i32)round(cast(f32)(1 << 6) * f);
}
u8 u8
DeMultiply(u8 color, u8 alpha) DeMultiply(u8 color, u8 alpha)
{ {
@ -217,180 +346,59 @@ TextSize(f32 s)
} }
FontAtlas FontAtlas
CreateAtlas(Arena* arena, FontFace font, u32 size, UVec2 atlas_dim) PopulateAtlasGlyphInfo(Arena* arena, FontFace* font, MSDFInfo* msdf_info)
{ {
FontAtlas atlas = { FontAtlas atlas;
data: Alloc!(u8)(arena, atlas_dim.x * atlas_dim.y * 4),
size: size,
dimensions: atlas_dim,
};
// TODO: proper packing algorithm // TODO: proper packing algorithm
if(font != null) if(font != null)
{ {
FT_Set_Pixel_Sizes(font, 0, TextSize(size)); i32 ascent, descent, line_gap;
stbtt_GetFontVMetrics(font, &ascent, &descent, &line_gap);
i64 bbox_ymax = font.bbox.yMax >> 6; i32 max_advance;
i64 bbox_ymin = font.bbox.yMin >> 6; foreach(i32 ch; 0 .. 128)
u64 baseline = font.size.metrics.ascender >> 6;
u64 line_height = (font.size.metrics.height >> 6)+1;
atlas.line_height = line_height;
atlas.max_advance = font.size.metrics.max_advance >> 6;
u32 max_w, max_h, count;
i32 font_size = Float26(size);
const u32 PADDING = 2;
foreach(FT_ULong char_code; 0 .. 0x7F)
{ {
FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); i32 glyph_index = stbtt_FindGlyphIndex(font, ch);
if(res != 0) if(glyph_index)
{ {
continue; i32 advance, x0, y0, x1, y1;
} stbtt_GetGlyphHMetrics(font, glyph_index, &advance, null);
stbtt_GetGlyphBox(font, glyph_index, &x0, &y0, &x1, &y1);
u32 bmp_w = font.glyph.bitmap.width; max_advance = max_advance < advance ? advance : max_advance;
u32 bmp_h = font.glyph.bitmap.rows;
if(max_w + bmp_w > atlas_dim.x)
{
max_h += line_height+PADDING;
max_w = 0;
}
if(max_h > atlas_dim.y) MSDFGlyph* msdfg;
foreach(j; 0 .. msdf_info.glyphs.length)
{ {
Logf("Unable to pack atlas within dimensions provided"); if(msdf_info.glyphs[j].glyph == ch)
assert(false);
}
max_w += bmp_w;
count += 1;
}
max_w = max_h = count = 0;
foreach(FT_ULong char_code; 0 .. 0x7F)
{ {
FT_Error res = FT_Load_Char(font, char_code, cast(FT_Int32)FT_LOAD_RENDER); msdfg = msdf_info.glyphs.ptr + j;
if(res != 0)
{
continue;
}
FT_GlyphSlot glyph = font.glyph;
FT_Bitmap* bmp = &font.glyph.bitmap;
if(max_w + bmp.rows > atlas_dim.x)
{
max_h += line_height+PADDING;
max_w = PADDING;
}
i64 base = Max(baseline, font.glyph.bitmap_top);
switch(bmp.pixel_mode)
{
case FT_PIXEL_MODE_BGRA:
{
u64 x, y;
foreach(r; 0 .. bmp.rows)
{
y = (base - font.glyph.bitmap_top + r) + max_h;
foreach(c; 0 .. bmp.width)
{
x = max_w + c;
u64 offset = (y*atlas_dim.y + x) * 4;
u8 p_b = bmp.buffer[offset+0];
u8 p_g = bmp.buffer[offset+1];
u8 p_r = bmp.buffer[offset+2];
u8 p_a = bmp.buffer[offset+3];
atlas.data[offset .. offset+4] = [DeMultiply(p_r, p_a), DeMultiply(p_b, p_a), DeMultiply(p_g, p_a), p_a];
}
}
} break;
case FT_PIXEL_MODE_MONO:
{
u32 pitch = bmp.pitch;
u8* buf = bmp.buffer;
u64 x, y;
foreach(r; 0 .. bmp.rows)
{
u8 bits = 0;
u8* buf_ptr = buf;
y = (base - font.glyph.bitmap_top + r) + max_h;
foreach(c; 0 .. bmp.width)
{
if((c & 7) == 0)
{
bits = *buf_ptr;
buf_ptr += 1;
}
x = max_w + c;
u64 offset = (y*atlas_dim.y + x) * 4;
atlas.data[offset .. offset+4] = [255, 255, 255, bits & 0x80 ? 255 : 0];
bits <<= 1;
}
buf += pitch;
}
} break;
case FT_PIXEL_MODE_GRAY:
{
u64 x, y;
foreach(r; 0 .. bmp.rows)
{
y = (base - font.glyph.bitmap_top + r) + max_h;
foreach(c; 0 .. bmp.width)
{
x = max_w + c;
u64 offset = (y*atlas_dim.y + x) * 4;
atlas.data[offset .. offset+4] = [255, 255, 255, bmp.buffer[r*bmp.pitch + c]];
}
}
} break;
default:
assert(false, "Unknown pixel_mode value");
break; break;
} }
Glyph* g = atlas.glyphs.ptr + char_code;
f32 height = glyph.metrics.height >> 6;
f32 width = glyph.metrics.width >> 6;
f32 top = font.glyph.bitmap_top;
f32 left = font.glyph.bitmap_left;
g.ch = cast(dchar)char_code;
g.advance = cast(f32)(glyph.advance.x >> 6);
g.plane_left = left;
g.plane_right = g.plane_left + bmp.width;
g.plane_top = 0.0;
g.plane_bottom = line_height;
g.atlas_top = max_h;
g.atlas_left = max_w;
g.atlas_bottom = max_h + line_height;
g.atlas_right = max_w + bmp.width;
max_w += bmp.width + PADDING;
count += 1;
} }
Glyph* g = atlas.glyphs.ptr + ch;
g.atlas = msdfg.atlas;
g.plane.left = cast(f32)x0;
g.plane.top = cast(f32)y0;
g.plane.right = cast(f32)x1;
g.plane.bottom = cast(f32)y1;
}
}
atlas.ascent = cast(f32)ascent;
atlas.descent = cast(f32)descent;
atlas.line_height = cast(f32)(ascent - descent); // Maybe add line gap?
atlas.line_gap = cast(f32)line_gap;
atlas.max_advance = cast(f32)max_advance;
} }
return atlas; return atlas;
} }
}
void void
BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info, u32* curve_index, stbtt_fontinfo* stb_font_info) BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info, u32* curve_index, stbtt_fontinfo* stb_font_info)
{ {
@ -424,7 +432,7 @@ BuildFontGlyph(Arena* arena, u32 index, u32 glyph_index, SlugFontInfo* font_info
f32 dx = x-cx; f32 dx = x-cx;
f32 dy = y-cy; f32 dy = y-cy;
if(Abs(dx) < 0.1 && Abs(dy) < 0.1) if(fabs(dx) < 0.1 && fabs(dy) < 0.1)
{ {
cx = x; cx = x;
cy = y; cy = y;

View File

@ -947,9 +947,7 @@ align(16) struct Matrix(T, int D)
template IsMatrixInstantiation(U) template IsMatrixInstantiation(U)
{ {
static void IsMatrix(T, int D)(Matrix!(T, D) x) {} enum bool IsMatrixInstantiation = isInstanceOf!(Matrix, U);
enum bool IsMatrixInstantiation = is(typeof(IsMatrix(U.init)));
} }
} }
@ -1556,7 +1554,9 @@ Clamp(T)(T value, T min, T max) if(IsVector!(T))
return value; return value;
} }
version(DLIB_TEST) unittest version(DLIB_TEST)
{
void DLibTestMath()
{ {
enum FLOAT_MAX = f32.max; enum FLOAT_MAX = f32.max;
enum FLOAT_MIN = -f32.max; enum FLOAT_MIN = -f32.max;
@ -1815,3 +1815,9 @@ version(DLIB_TEST) unittest
assert(Clamp(-20, -5, 55) == -5); assert(Clamp(-20, -5, 55) == -5);
} }
} }
unittest
{
DLibTestMath();
}
}

View File

@ -16,15 +16,3 @@ else
public import dlibincludes; public import dlibincludes;
} }
shared static this()
{
static if(NativeTarget)
{
FT_Init_FreeType(&FT_LIB);
}
else
{
ta_init(256, 16, (void*).sizeof*2);
}
}

View File

@ -17,7 +17,8 @@ static if(NativeTarget)
import dlib.platform; import dlib.platform;
} }
enum bool StringType(T) = (is(T: string) || is(T: u8[]) || is(T: char[]) || is(T: const(char)[])); enum bool StringType(T) = (is(T == string) || is(T == u8[]) || is(T == char[]) || is(T == const(char)[]));
enum bool CStringType(T) = (is(T == char*) || is(T == u8*) || is(T == immutable(char)*) || is(T == const(char)*));
pragma(inline) void pragma(inline) void
Int3() Int3()
@ -59,6 +60,12 @@ Str(T)(T arr) if(StringType!(T))
return (cast(immutable(char)*)arr.ptr)[0 .. arr.length]; return (cast(immutable(char)*)arr.ptr)[0 .. arr.length];
} }
string
Str(T)(T ptr) if(CStringType!(T))
{
return ptr ? Str(ptr[0 .. cast(usize)strlen(ptr)]) : null;
}
alias ConvToStr = Str; alias ConvToStr = Str;
T[] T[]
@ -1120,7 +1127,9 @@ ProcessHash(const(void)* data, ref u64 state_0, ref u64 state_1, ref u64 state_2
state_3 = ProcessSingleHashValue(state_3, block[3]); state_3 = ProcessSingleHashValue(state_3, block[3]);
} }
version(DLIB_TEST) unittest version(DLIB_TEST)
{
void DLibTestUtil()
{ {
{ // Singly Linked List { // Singly Linked List
alias LT = ListBox!(u32, false); alias LT = ListBox!(u32, false);
@ -1358,7 +1367,6 @@ version(DLIB_TEST) unittest
ResetScratch(MB(4)); ResetScratch(MB(4));
string str = Scratchf("%s testing %s", 55, "testing"); string str = Scratchf("%s testing %s", 55, "testing");
assert(str == "55 testing testing"); assert(str == "55 testing testing");
Logf(str);
} }
{ {
@ -1390,3 +1398,9 @@ version(DLIB_TEST) unittest
} }
} }
} }
unittest
{
DLibTestUtil();
}
}

4414
stb_truetype.d Normal file

File diff suppressed because it is too large Load Diff

10
test.sh
View File

@ -2,17 +2,17 @@
name="Test_Runner" name="Test_Runner"
shared_src="dlib/package.d dlib/platform.d dlib/fonts.d dlib/aliases.d dlib/math.d dlib/util.d dlib/alloc.d dlib/assets.d dlib/externdecl.d" shared_src="dlib/package.d dlib/platform.d dlib/fonts.d dlib/aliases.d dlib/math.d dlib/util.d dlib/alloc.d dlib/assets.d dlib/externdecl.d stb_truetype.d"
if [ "$1" == "wasm" ]; then if [ "$1" == "wasm" ]; then
flags="-c -vgc -mtriple=wasm32-unknown-unknown-wasm -d-version=DLIB_TEST -fvisibility=public -dllimport=all --unittest --Xcc=-DBUILD_WASM -Iwasm/runtime -L--no-entry -i=core -i=std -i=. --real-precision=double --of=build/dlibmain.wasm --d-version=inline_concat -verrors=90" flags="-vgc -betterC -mtriple=wasm32-unknown-unknown-wasm -d-version=DLIB_TEST -fvisibility=hidden --unittest -Iwasm/runtime -i=core -i=std -i=. --real-precision=double --of=build/dlibmain.wasm --d-version=inline_concat -verrors=90 --of=build/dlib.wasm -gcc=clang"
wasm_src="wasm/runtime/object.d" wasm_src="wasm/runtime/object.d stb_truetype.d"
/bin/bash ./build.sh build wasm #/bin/bash ./build.sh build wasm
ldc2 $flags $shared_src $wasm_src ldc2 $flags $shared_src $wasm_src
wasm-ld build/dlibmain.wasm build/dlibincludes.wasm --export=RunTests --error-limit=0 --export-memory -obuild/dlib.wasm #wasm-ld build/dlibmain.wasm build/dlibincludes.wasm --export=RunTests --error-limit=0 --export-memory -obuild/dlib.wasm
cp build/dlib.wasm wasm/dlib.wasm cp build/dlib.wasm wasm/dlib.wasm
else else

26
test/teststbtt.d Normal file
View File

@ -0,0 +1,26 @@
import stb_truetype;
import core.stdc.stdio;
import core.stdc.stdlib;
ubyte[] FONT_DATA = cast(ubyte[])import("pc-9800.ttf");
stbtt_bakedchar[96] cdata;
void
main(string[] args)
{
char arg1 = args.length > 1 ? args[1][0] : 'a';
stbtt_fontinfo font;
ubyte* bitmap;
int w,h,i,j,c = (arg1), s = (args.length > 2 ? atoi(args[2].ptr) : 20);
stbtt_InitFont(&font, FONT_DATA.ptr, stbtt_GetFontOffsetForIndex(FONT_DATA.ptr, 0));
bitmap = stbtt_GetCodepointBitmap(&font, 0, stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, null, null);
for (j=0; j < h; ++j) {
for (i=0; i < w; ++i)
putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
putchar('\n');
}
}

Binary file not shown.

View File

@ -1,5 +1,6 @@
module core.arsd.objectutils; module core.arsd.objectutils;
/*
///Provides only __doPostblit and hasPostblit for making the code simpler. ///Provides only __doPostblit and hasPostblit for making the code simpler.
size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
{ {
@ -11,6 +12,9 @@ size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc
} }
return 0; return 0;
} }
*/
/*
// strip const/immutable/shared/inout from type info // strip const/immutable/shared/inout from type info
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
{ {
@ -32,6 +36,7 @@ inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
} }
return ti; return ti;
} }
*/
bool hasPostblit(in TypeInfo ti) nothrow pure bool hasPostblit(in TypeInfo ti) nothrow pure
{ {

View File

@ -157,10 +157,10 @@ void* _d_cast(To, From)(From o) @trusted
{ {
return _d_paint_cast!To(o); return _d_paint_cast!To(o);
} }
else static if (is (To : From)) //else static if (is (To : From))
{ //{
return _d_class_cast!To(o); // return _d_class_cast!To(o);
} //}
else else
{ {
return null; return null;

View File

@ -0,0 +1,4 @@
module core.stdc.math;
public import wasm : pow, cos, acos;

View File

@ -1,10 +1,13 @@
module core.math; module core.math;
pragma(LDC_intrinsic, "llvm.sqrt.f32") pragma(LDC_intrinsic, "llvm.sqrt.f#") T
float llvm_sqrt(float x) pure nothrow @nogc @safe; llvm_sqrt(T)(T x) pure nothrow @nogc @safe;
pragma(LDC_intrinsic, "llvm.sqrt.f64")
double llvm_sqrt(double x) pure nothrow @nogc @safe;
float sqrt(float x ) => x < 0.0f ? float.nan : llvm_sqrt(x); float sqrt(float x ) => x < 0.0f ? float.nan : llvm_sqrt(x);
double sqrt(double x) => x < 0.0f ? double.nan : llvm_sqrt(x); double sqrt(double x) => x < 0.0f ? double.nan : llvm_sqrt(x);
pragma(LDC_intrinsic, "llvm.fabs.f#") T
llvm_fabs(T)(T x) pure nothrow @nogc @safe;
float fabs(float x) => llvm_fabs(x);

View File

@ -12,10 +12,18 @@ alias AliasSeq(TList...) = TList;
// import core.arsd.memory_allocation; // import core.arsd.memory_allocation;
import core.stdc.string; import core.stdc.string;
import dlib.externdecl; import dlib.alloc;
import dlib.util;
import wasm; import wasm;
export extern(C) void
__assert(const(char)* msg, const(char)* file, uint line)
{
Abortf("%s(%s): Assertion failure %s", Str(file), line, Str(msg));
}
version(CarelessAlocation) version(CarelessAlocation)
{ {
version = inline_concat; version = inline_concat;
@ -129,7 +137,7 @@ class TypeInfo_AssociativeArray : TypeInfo
version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2)
{ {
arg1 = typeid(void*); //arg1 = typeid(void*);
return 0; return 0;
} }
} }
@ -172,7 +180,8 @@ template _arrayOp(Args...)
alias _arrayOp = arrayOp!Args; alias _arrayOp = arrayOp!Args;
} }
extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsz) { extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen, size_t elemsz)
{
auto d = cast(ubyte*) dst; auto d = cast(ubyte*) dst;
auto s = cast(ubyte*) src; auto s = cast(ubyte*) src;
auto len = dstlen * elemsz; auto len = dstlen * elemsz;
@ -183,10 +192,10 @@ extern(C) void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t s
s++; s++;
len--; len--;
} }
} }
void reserve(T)(ref T[] arr, size_t length) @trusted { void reserve(T)(ref T[] arr, size_t length) @trusted
{
arr = (cast(T*) (malloc(length * T.sizeof)))[0 .. 0]; arr = (cast(T*) (malloc(length * T.sizeof)))[0 .. 0];
} }
@ -194,37 +203,37 @@ void reserve(T)(ref T[] arr, size_t length) @trusted {
extern(C) void _d_arraybounds(string file, size_t line) extern(C) void _d_arraybounds(string file, size_t line)
{ {
Abort("Range Error"); Abortf("%s(%s): Range Error", file, line);
} }
/// Called when an out of range slice of an array is created /// Called when an out of range slice of an array is created
extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length) extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length)
{ {
Abort("Range error on slice creation"); Abortf("%s(%s): Range error on slice creation %s is out of range on [%s .. %s]", file, line, length, lwr, upr);
} }
/// Called when an out of range array index is accessed /// Called when an out of range array index is accessed
extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length) extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length)
{ {
Abort("Range error on indexing array"); Abortf("%s(%s): Range error on indexing array %s is out of range of %s", file, line, index, length);
} }
// } // }
extern(C) void _d_assert(string file, uint line) @trusted @nogc extern(C) void _d_assert(string file, uint line) @trusted @nogc
{ {
Abort("Assertion failure"); Abortf("%s(%s): Assertion failure", file, line);
} }
void _d_assertp(immutable(char)* file, uint line) void _d_assertp(immutable(char)* file, uint line)
{ {
Abort("Assertion failure"); Abortf("%s(%s): Assertion failure", Str(file), line);
} }
extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc
{ {
Abort("Assertion failure with msg"); Abortf("%s(%s): Assertion failure - %s", file, line, msg);
} }
void __switch_error(string file, size_t line) @trusted @nogc void __switch_error(string file, size_t line) @trusted @nogc
@ -232,7 +241,8 @@ void __switch_error(string file, size_t line) @trusted @nogc
_d_assert_msg("final switch error", file, line); _d_assert_msg("final switch error", file, line);
} }
bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) { bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
{
if (lhs.length != rhs.length) if (lhs.length != rhs.length)
{ {
return false; return false;
@ -252,7 +262,8 @@ bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) {
// bare basics class support { // bare basics class support {
extern(C) Object _d_allocclass(TypeInfo_Class ti) { extern(C) Object _d_allocclass(TypeInfo_Class ti)
{
auto ptr = (cast(byte*)malloc(ti.m_init.length))[0 .. ti.m_init.length]; auto ptr = (cast(byte*)malloc(ti.m_init.length))[0 .. ti.m_init.length];
ptr[0 .. $] = ti.m_init[0 .. $]; ptr[0 .. $] = ti.m_init[0 .. $];
return cast(Object) ptr.ptr; return cast(Object) ptr.ptr;
@ -1962,11 +1973,13 @@ extern(C) void[] _d_newarrayiT(const TypeInfo ti, size_t length)
} }
} }
/*
extern (C) void* _d_newitemU(scope const TypeInfo _ti) extern (C) void* _d_newitemU(scope const TypeInfo _ti)
{ {
auto ti = unqualify(_ti); auto ti = unqualify(_ti);
return malloc(ti.tsize); return malloc(ti.tsize);
} }
*/
/// ditto /// ditto
extern (C) T* _d_newitemT(T)() @trusted extern (C) T* _d_newitemT(T)() @trusted
@ -1979,6 +1992,7 @@ extern (C) T* _d_newitemT(T)() @trusted
return p; return p;
} }
/*
/// Same as above, for item with non-zero initializer. /// Same as above, for item with non-zero initializer.
extern (C) void* _d_newitemiT(TypeInfo ti) extern (C) void* _d_newitemiT(TypeInfo ti)
{ {
@ -1987,8 +2001,7 @@ extern (C) void* _d_newitemiT(TypeInfo ti)
memcpy(p, initializer.ptr, initializer.length); memcpy(p, initializer.ptr, initializer.length);
return p; return p;
} }
*/
private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions) private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dimensions)
{ {
@ -2133,6 +2146,7 @@ extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y)
return p[0 .. x.length + y.length]; return p[0 .. x.length + y.length];
} }
/*
extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
{ {
// c could encode into from 1 to 4 characters // c could encode into from 1 to 4 characters
@ -2174,6 +2188,7 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c)
// //
return _d_arrayappendT(typeid(shared char[]), x, appendthis); return _d_arrayappendT(typeid(shared char[]), x, appendthis);
} }
*/
@ -3103,6 +3118,7 @@ class Exception : Throwable
} }
} }
/*
inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
{ {
TypeInfo ti = cast() cti; TypeInfo ti = cast() cti;
@ -3123,6 +3139,7 @@ inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc
} }
return ti; return ti;
} }
*/
private void _doPostblit(T)(T[] arr) private void _doPostblit(T)(T[] arr)
{ {

View File

@ -5,7 +5,7 @@ import std.traits;
import wasm; import wasm;
char[] char[]
sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args args) sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args args) @nogc
{ {
SprintfType type = SprintfType.None; SprintfType type = SprintfType.None;
static foreach(i; 0 .. Args.length) static foreach(i; 0 .. Args.length)
@ -52,12 +52,20 @@ sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args ar
else static if(is(Args[i] == double)) type = F64; else static if(is(Args[i] == double)) type = F64;
else static if(is(Args[i] == bool)) type = Bool; else static if(is(Args[i] == bool)) type = Bool;
else static if(is(Args[i] == char)) type = Char; else static if(is(Args[i] == char)) type = Char;
else static if(isPointer!(Args[i])) type = Pointer;
else static assert(false, "Type unsupported"); else static assert(false, "Type unsupported");
} }
static if(isPointer!(Args[i]))
{
SprintfLoadValue(args[i], type);
}
else
{
SprintfLoadValue(&args[i], type); SprintfLoadValue(&args[i], type);
} }
} }
}
size_t length = SprintfEnd(buf, Str(fmt)); size_t length = SprintfEnd(buf, Str(fmt));
@ -65,7 +73,7 @@ sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args ar
} }
char[] char[]
sformat(alias fmt, Args...)(char[] buf, Args args) if(isSomeString!(typeof(fmt))) sformat(alias fmt, Args...)(char[] buf, Args args) @nogc if(isSomeString!(typeof(fmt)))
{ {
return .sformat(buf, fmt, args); return .sformat(buf, fmt, args);
} }

View File

@ -6,3 +6,17 @@ if (__traits(isFloating, T));
float round(float x) => llvm_round(x); float round(float x) => llvm_round(x);
double round(double x) => llvm_round(x); double round(double x) => llvm_round(x);
pragma(LDC_intrinsic, "llvm.floor.f#")
T llvm_floor(T)(T val)
if(__traits(isFloating, T));
float floor(float x) => llvm_floor(x);
double floor(double x) => llvm_floor(x);
pragma(LDC_intrisic, "llvm.ceil.f#")
T llvm_ceil(T)(T val)
if(__traits(isFloating, T));
float ceil(float x) => llvm_ceil(x);
double ceil(double x) => llvm_ceil(x);

View File

@ -1,5 +1,7 @@
module std.traits; module std.traits;
public import core.internal.traits;
enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real); enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real);
template isIntegral(T) template isIntegral(T)
@ -82,3 +84,11 @@ template isAggregateType(T)
} }
enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar));
template Select(bool condition, T...)
if (T.length == 2)
{
import std.meta : Alias;
alias Select = Alias!(T[!condition]);
}

View File

@ -2,6 +2,9 @@ module wasm;
import ldc.attributes; import ldc.attributes;
import dlib.util;
import std.format;
enum SprintfType : size_t enum SprintfType : size_t
{ {
None, None,
@ -30,9 +33,12 @@ enum SprintfType : size_t
F64Array, F64Array,
Char, Char,
CharArray, CharArray,
Pointer,
} }
extern extern(C) @nogc @llvmAttr("wasm-import-module", "env"):
extern extern(C) @llvmAttr("wasm-import-module", "env"):
@llvmAttr("wasm-import-name", "Console") void @llvmAttr("wasm-import-name", "Console") void
Console(string str, bool write_line); Console(string str, bool write_line);
@ -41,39 +47,60 @@ Console(string str, bool write_line);
Console2(size_t length, const(void)* ptr, bool write_line); Console2(size_t length, const(void)* ptr, bool write_line);
@llvmAttr("wasm-import-name", "Abort") void @llvmAttr("wasm-import-name", "Abort") void
Abort(string message); Abort(string message) @nogc;
@llvmAttr("wasm-import-name", "SprintfLoadValue") void @llvmAttr("wasm-import-name", "SprintfLoadValue") void
SprintfLoadValue(const(void)* ptr, SprintfType type); SprintfLoadValue(const(void)* ptr, SprintfType type) @nogc;
@llvmAttr("wasm-import-name", "SprintfLoadArray") void @llvmAttr("wasm-import-name", "SprintfLoadArray") void
SprintfLoadArray(size_t length, const(void)* ptr, SprintfType type); SprintfLoadArray(size_t length, const(void)* ptr, SprintfType type) @nogc;
@llvmAttr("wasm-import-name", "SprintfEnd") size_t @llvmAttr("wasm-import-name", "SprintfEnd") size_t
SprintfEnd(char[] buffer, string format); SprintfEnd(char[] buffer, string format) @nogc;
@llvmAttr("wasm-import-name", "pow") double
pow(double base, double exponent) @nogc;
@llvmAttr("wasm-import-name", "cos") double
cos(double x) @nogc;
@llvmAttr("wasm-import-name", "acos") double
acos(double x) @nogc;
void
Abortf(Args...)(string fmt, Args args) @nogc
{
char[1024] buffer;
string abort_message = Str(sformat(buffer, fmt, args));
Abort(abort_message);
}
export void export void
_start() _start()
{ {
import dlib.alloc; import dlib.alloc;
import dlib.util; import dlib.util;
import std.format;
MallocInit(256, 16, (void*).sizeof*2);
ResetScratch(MB(2)); ResetScratch(MB(2));
char[100] buffer;
uint[2] arr = [1, 2];
sformat(buffer, "%s", arr);
} }
version(DLIB_TEST) version(DLIB_TEST) export void
{
export void
RunTests() RunTests()
{ {
Console("azZ", true); import dlib;
static foreach(test_fn; __traits(getUnitTests, __traits(parent, _start)))
{ DLibTestMath();
Console("Running test", true); DLibTestUtil();
test_fn(); DLibTestAlloc();
}
Console("Tests succeeded", true); Console("Tests succeeded", true);
} }
}

View File

@ -27,6 +27,8 @@ const TYPES = {
CharArray: 25, CharArray: 25,
}; };
let LoadSize = null;
function assert(condition, message) function assert(condition, message)
{ {
if(!condition) if(!condition)
@ -36,118 +38,131 @@ function assert(condition, message)
} }
} }
class WasmManager const wasm_manager = {
{ memory: null,
constructor(memory, ptr_size = 4) data_view: null,
{ stdout_string: "",
this.ptr_size = ptr_size; sprintf_values: [],
ptr_size: 4,
};
this.sprintf_values = []; function InitWasmManager(memory, ptr_size = 4)
this.stdout_string = "";
switch(this.ptr_size)
{ {
case 8: this.LoadSize = this.LoadU64; break; wasm_manager.ptr_size = ptr_size;
wasm_manager.sprintf_values = [];
wasm_manager.stdout_string = "";
switch(wasm_manager.ptr_size)
{
case 8: LoadSize = LoadU64; break;
case 4: case 4:
default: this.LoadSize = this.LoadU32; break; default: LoadSize = LoadU32; break;
} }
} }
SetMemory(memory) function SetMemory(memory)
{ {
this.memory = memory; wasm_manager.memory = memory;
this.data_view = new DataView(this.memory.buffer); wasm_manager.data_view = new DataView(wasm_manager.memory.buffer);
} }
LoadBool = (addr) => Boolean(this.data_view.getUint8(addr, true)); function Memory()
LoadF32 = (addr) => this.data_view.getFloat32(addr, true);
LoadF64 = (addr) => this.data_view.getFloat64(addr, true);
LoadU8 = (addr) => this.data_view.getUint8 (addr, true);
LoadI8 = (addr) => this.data_view.getInt8 (addr, true);
LoadU16 = (addr) => this.data_view.getUint16 (addr, true);
LoadI16 = (addr) => this.data_view.getInt16 (addr, true);
LoadU32 = (addr) => this.data_view.getUint32 (addr, true);
LoadI32 = (addr) => this.data_view.getInt32 (addr, true);
LoadU64(addr)
{ {
const lo = this.data_view.getUint32(addr+0, true); return new DataView(wasm_manager.memory.buffer);
const hi = this.data_view.getUint32(addr+4, true); }
function LoadBool(addr) { return Boolean(Memory().getUint8(addr, true)); }
function LoadF32(addr) { return Memory().getFloat32(addr, true); }
function LoadF64(addr) { return Memory().getFloat64(addr, true); }
function LoadU8 (addr) { return Memory().getUint8 (addr, true); }
function LoadI8 (addr) { return Memory().getInt8 (addr, true); }
function LoadU16(addr) { return Memory().getUint16 (addr, true); }
function LoadI16(addr) { return Memory().getInt16 (addr, true); }
function LoadU32(addr) { return Memory().getUint32 (addr, true); }
function LoadI32(addr) { return Memory().getInt32 (addr, true); }
function LoadU64(addr)
{
const lo = Memory().getUint32(addr+0, true);
const hi = Memory().getUint32(addr+4, true);
return lo + hi*4294967296; return lo + hi*4294967296;
} }
LoadI64(addr) function LoadI64(addr)
{ {
const lo = this.data_view.getUint32(addr+0, true); const lo = Memory().getUint32(addr+0, true);
const hi = this.data_view.getInt32 (addr+4, true); const hi = Memory().getInt32 (addr+4, true);
return lo + hi*4294967296; return lo + hi*4294967296;
} }
LoadArray = (array_class, length, addr) => new array_class(this.memory.buffer, addr, length); function LoadArray(array_class, length, addr)
LoadString = (length, addr) => new TextDecoder().decode(this.LoadArray(Uint8Array, length, addr)); {
return new array_class(wasm_manager.memory.buffer, addr, length);
}
LoadCString(ptr) function LoadString(length, addr)
{
return new TextDecoder().decode(LoadArray(Uint8Array, length, addr));
}
function LoadCString(ptr)
{ {
if(!ptr) return null; if(!ptr) return null;
let length = 0; let length = 0;
for(; this.LoadU8(ptr+length); length += 1) {} for(; LoadU8(ptr+length); length += 1) {}
return this.LoadString(ptr, length); return LoadString(ptr, length);
} }
StoreString(ptr, value) function StoreString(ptr, value)
{ {
const src = new TextEncoder().encode(value); const src = new TextEncoder().encode(value);
const dst = new Uint8Array(this.memory.buffer, ptr, src.length); const dst = new Uint8Array(wasm_manager.memory.buffer, ptr, src.length);
dst.set(src); dst.set(src);
return src.length; return src.length;
} }
Imports() const imports = {
{
const manager = this;
return {
env: { env: {
Abort(string_length, string_ptr) Abort(string_length, string_ptr)
{ {
console.error(manager.LoadString(string_length, string_ptr)); console.error(LoadString(string_length, string_ptr));
}, },
SprintfLoadValue(ptr, type) SprintfLoadValue(ptr, type)
{ {
let value = null; let value = null;
switch(type) switch(type)
{ {
case TYPES.U8: value = manager.LoadU8(ptr); break; case TYPES.U8: value = LoadU8(ptr); break;
case TYPES.I8: value = manager.LoadI8(ptr); break; case TYPES.I8: value = LoadI8(ptr); break;
case TYPES.U16: value = manager.LoadU16(ptr); break; case TYPES.U16: value = LoadU16(ptr); break;
case TYPES.I16: value = manager.LoadI16(ptr); break; case TYPES.I16: value = LoadI16(ptr); break;
case TYPES.U32: value = manager.LoadU32(ptr); break; case TYPES.U32: value = LoadU32(ptr); break;
case TYPES.I32: value = manager.LoadI32(ptr); break; case TYPES.I32: value = LoadI32(ptr); break;
case TYPES.U64: value = manager.LoadU64(ptr); break; case TYPES.U64: value = LoadU64(ptr); break;
case TYPES.I64: value = manager.LoadI64(ptr); break; case TYPES.I64: value = LoadI64(ptr); break;
case TYPES.SizeT: value = manager.LoadSize(ptr); break; case TYPES.SizeT: value = LoadSize(ptr); break;
case TYPES.F32: value = manager.LoadF32(ptr); break; case TYPES.F32: value = LoadF32(ptr); break;
case TYPES.F64: value = manager.LoadF64(ptr); break; case TYPES.F64: value = LoadF64(ptr); break;
case TYPES.Bool: value = manager.LoadBool(ptr); break; case TYPES.Bool: value = LoadBool(ptr); break;
case TYPES.Char: value = manager.LoadString(1, ptr); break; case TYPES.Char: value = LoadString(1, ptr); break;
default: break; default: break;
} }
if(value) if(value != null)
{ {
if(type === TYPES.Bool) if(type === TYPES.Bool)
{ {
manager.sprintf_values.push(value ? 'true' : 'false'); wasm_manager.sprintf_values.push(value ? 'true' : 'false');
} }
else else
{ {
manager.sprintf_values.push(''+value); wasm_manager.sprintf_values.push(''+value);
} }
} }
}, },
@ -157,17 +172,17 @@ class WasmManager
switch(type) switch(type)
{ {
case TYPES.CharArray: case TYPES.CharArray:
case TYPES.String: value = manager.LoadString(length, ptr); break; case TYPES.String: value = LoadString(length, ptr); break;
case TYPES.U8Array: value = manager.LoadArray(Uint8Array, length, ptr); break; case TYPES.U8Array: value = LoadArray(Uint8Array, length, ptr); break;
case TYPES.I8Array: value = manager.LoadArray(Int8Array, length, ptr); break; case TYPES.I8Array: value = LoadArray(Int8Array, length, ptr); break;
case TYPES.U16Array: value = manager.LoadArray(Uint16Array, length, ptr); break; case TYPES.U16Array: value = LoadArray(Uint16Array, length, ptr); break;
case TYPES.I16Array: value = manager.LoadArray(Int16Array, length, ptr); break; case TYPES.I16Array: value = LoadArray(Int16Array, length, ptr); break;
case TYPES.U32Array: value = manager.LoadArray(Uint32Array, length, ptr); break; case TYPES.U32Array: value = LoadArray(Uint32Array, length, ptr); break;
case TYPES.I32Array: value = manager.LoadArray(Int32Array, length, ptr); break; case TYPES.I32Array: value = LoadArray(Int32Array, length, ptr); break;
case TYPES.U64Array: value = manager.LoadArray(BigUint64Array, length, ptr); break; case TYPES.U64Array: value = LoadArray(BigUint64Array, length, ptr); break;
case TYPES.I64Array: value = manager.LoadArray(BigInt64Array, length, ptr); break; case TYPES.I64Array: value = LoadArray(BigInt64Array, length, ptr); break;
case TYPES.F32Array: value = manager.LoadArray(Float32Array, length, ptr); break; case TYPES.F32Array: value = LoadArray(Float32Array, length, ptr); break;
case TYPES.F64Array: value = manager.LoadArray(Float64Array, length, ptr); break; case TYPES.F64Array: value = LoadArray(Float64Array, length, ptr); break;
} }
if(value) if(value)
@ -177,12 +192,12 @@ class WasmManager
value = `[${value.join(", ")}]`; value = `[${value.join(", ")}]`;
} }
manager.sprintf_values.push(value); wasm_manager.sprintf_values.push(value);
} }
}, },
SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr) SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr)
{ {
let format_string = manager.LoadString(string_length, string_ptr); let format_string = LoadString(string_length, string_ptr);
let index = format_string.indexOf("%"); let index = format_string.indexOf("%");
@ -197,7 +212,7 @@ class WasmManager
} }
let value_index = 0; let value_index = 0;
for(; value_index < manager.sprintf_values.length;) for(; value_index < wasm_manager.sprintf_values.length;)
{ {
assert(format.length); assert(format.length);
@ -225,7 +240,7 @@ class WasmManager
continue; continue;
} }
result += manager.sprintf_values[value_index++]; result += wasm_manager.sprintf_values[value_index++];
format = format.substring(digit_index); format = format.substring(digit_index);
Next(); Next();
@ -234,7 +249,7 @@ class WasmManager
} }
else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')) else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z'))
{ {
result += manager.sprintf_values[value_index++]; result += wasm_manager.sprintf_values[value_index++];
format = format.substring(2); format = format.substring(2);
Next(); Next();
} }
@ -243,53 +258,52 @@ class WasmManager
result += format; result += format;
const bytes_written = manager.StoreString(buffer_ptr, result); const bytes_written = StoreString(buffer_ptr, result);
assert(bytes_written <= buffer_length, "Format string is longer than buffer length"); assert(bytes_written <= buffer_length, "Format string is longer than buffer length");
manager.sprintf_values = []; wasm_manager.sprintf_values = [];
return bytes_written; return bytes_written;
}, },
Console(string_length, string_ptr, write_line) Console(string_length, string_ptr, write_line)
{ {
manager.stdout_string += manager.LoadString(string_length, string_ptr); wasm_manager.stdout_string += LoadString(string_length, string_ptr);
const arr = manager.LoadArray(Uint8Array, string_length, string_ptr); const arr = LoadArray(Uint8Array, string_length, string_ptr);
if(write_line || manager.stdout_string.includes("\n")) if(write_line || wasm_manager.stdout_string.includes("\n"))
{ {
console.log(manager.stdout_string); console.log(wasm_manager.stdout_string);
manager.stdout_string = ""; wasm_manager.stdout_string = "";
} }
}, },
Console2(string_length, string_ptr, write_line) Console2(string_length, string_ptr, write_line)
{ {
manager.stdout_string += manager.LoadString(string_length, string_ptr); wasm_manager.stdout_string += LoadString(string_length, string_ptr);
const arr = manager.LoadArray(Uint8Array, string_length, string_ptr); const arr = LoadArray(Uint8Array, string_length, string_ptr);
if(write_line || manager.stdout_string.includes("\n")) if(write_line || wasm_manager.stdout_string.includes("\n"))
{ {
console.log(manager.stdout_string); console.log(wasm_manager.stdout_string);
manager.stdout_string = ""; wasm_manager.stdout_string = "";
} }
}, },
pow: Math.pow,
cos: Math.cos,
acos: Math.acos,
}, },
} }
}
}
async function StartWasm(path) async function StartWasm(path)
{ {
const manager = new WasmManager();
const response = await fetch(path); const response = await fetch(path);
const data = await response.arrayBuffer(); const data = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(data, manager.Imports()); const wasm = await WebAssembly.instantiate(data, imports);
const exports = wasm.instance.exports; const exports = wasm.instance.exports;
if(exports.memory) if(exports.memory)
{ {
manager.SetMemory(exports.memory); SetMemory(exports.memory);
} }
exports?._start(); exports?._start();