major project restructure
This commit is contained in:
parent
bd31f562fc
commit
4f7fa7d50c
67 changed files with 25 additions and 2427 deletions
227
src/font.c3
Normal file
227
src/font.c3
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
module ugui::font;
|
||||
|
||||
import schrift;
|
||||
import std::collections::map;
|
||||
import std::core::mem;
|
||||
import std::core::mem::allocator;
|
||||
import std::io;
|
||||
import std::ascii;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// CODEPOINT //
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
|
||||
// unicode code point, different type for a different hash
|
||||
alias Codepoint = uint;
|
||||
|
||||
//macro uint Codepoint.hash(self) => ((uint)self).hash();
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// FONT ATLAS //
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
|
||||
/* width and height of a glyph contain the kering advance
|
||||
* (u,v)
|
||||
* +-------------*---+ -
|
||||
* | ^ | | ^
|
||||
* | |oy | | |
|
||||
* | v | | |
|
||||
* | .ii. | | |
|
||||
* | @@@@@@. | | |
|
||||
* | V@Mio@@o | | |
|
||||
* | :i. V@V | | h
|
||||
* | :oM@@M | | |
|
||||
* | :@@@MM@M | | |
|
||||
* | @@o o@M | | |
|
||||
* |<->:@@. M@M | | |
|
||||
* |ox @@@o@@@@ | | |
|
||||
* | :M@@V:@@.| | v
|
||||
* +-------------*---+ -
|
||||
* |<---- w ---->|
|
||||
* |<------ adv ---->|
|
||||
*/
|
||||
struct Glyph {
|
||||
Codepoint code;
|
||||
ushort u, v;
|
||||
ushort w, h;
|
||||
short adv, ox, oy;
|
||||
}
|
||||
|
||||
const uint FONT_CACHED = 255;
|
||||
alias GlyphTable = map::HashMap{Codepoint, Glyph};
|
||||
|
||||
faultdef TTF_LOAD_FAILED, MISSING_GLYPH, BAD_GLYPH_METRICS, RENDER_ERROR;
|
||||
|
||||
struct Font {
|
||||
schrift::Sft sft;
|
||||
String path;
|
||||
Id id; // font id, same as atlas id
|
||||
GlyphTable table;
|
||||
|
||||
float size;
|
||||
float ascender, descender, linegap; // Line Metrics
|
||||
Atlas atlas;
|
||||
bool should_update; // should send update_atlas command, resets at frame_end()
|
||||
}
|
||||
|
||||
macro Rect Glyph.bounds(&g) => {.x = g.ox, .y = g.oy, .w = g.w, .h = g.h};
|
||||
macro Rect Glyph.uv(&g) => {.x = g.u, .y = g.v, .w = g.w, .h = g.h};
|
||||
|
||||
<*
|
||||
@param [&inout] font
|
||||
@param [in] name
|
||||
@param [&in] path
|
||||
@require height > 0, scale > 0: "height and scale must be positive non-zero"
|
||||
*>
|
||||
fn void? Font.load(&font, Allocator allocator, String name, ZString path, uint height, float scale)
|
||||
{
|
||||
font.table.init(allocator, capacity: FONT_CACHED);
|
||||
font.id = name.hash();
|
||||
|
||||
font.size = height*scale;
|
||||
|
||||
font.sft = {
|
||||
.xScale = (double)font.size,
|
||||
.yScale = (double)font.size,
|
||||
.flags = schrift::SFT_DOWNWARD_Y,
|
||||
};
|
||||
|
||||
font.sft.font = schrift::loadfile(path);
|
||||
if (font.sft.font == null) {
|
||||
font.table.free();
|
||||
return TTF_LOAD_FAILED?;
|
||||
}
|
||||
|
||||
schrift::SftLMetrics lmetrics;
|
||||
schrift::lmetrics(&font.sft, &lmetrics);
|
||||
font.ascender = (float)lmetrics.ascender;
|
||||
font.descender = (float)lmetrics.descender;
|
||||
font.linegap = (float)lmetrics.lineGap;
|
||||
//io::printfn("ascender:%d, descender:%d, linegap:%d", font.ascender, font.descender, font.linegap);
|
||||
|
||||
// TODO: allocate buffer based on FONT_CACHED and the size of a sample letter
|
||||
// like the letter 'A'
|
||||
ushort size = (ushort)font.size*(ushort)($$sqrt((float)FONT_CACHED));
|
||||
font.atlas.new(font.id, ATLAS_GRAYSCALE, size, size)!;
|
||||
|
||||
// preallocate the ASCII range
|
||||
for (char c = ' '; c < '~'; c++) {
|
||||
// FIXME: without @inline, this crashes with O1 or greater
|
||||
font.get_glyph((Codepoint)c) @inline!;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] font
|
||||
*>
|
||||
fn Glyph*? Font.get_glyph(&font, Codepoint code)
|
||||
{
|
||||
Glyph*? gp;
|
||||
gp = font.table.get_ref(code);
|
||||
|
||||
if (catch excuse = gp) {
|
||||
if (excuse != NOT_FOUND) {
|
||||
return excuse?;
|
||||
}
|
||||
} else {
|
||||
return gp;
|
||||
}
|
||||
|
||||
|
||||
// missing glyph, render and place into an atlas
|
||||
Glyph glyph;
|
||||
|
||||
schrift::SftGlyph gid;
|
||||
schrift::SftGMetrics gmtx;
|
||||
|
||||
if (schrift::lookup(&font.sft, (SftUChar)code, &gid) < 0) {
|
||||
return MISSING_GLYPH?;
|
||||
}
|
||||
|
||||
if (schrift::gmetrics(&font.sft, gid, &gmtx) < 0) {
|
||||
return BAD_GLYPH_METRICS?;
|
||||
}
|
||||
|
||||
schrift::SftImage img = {
|
||||
.width = gmtx.minWidth,
|
||||
.height = gmtx.minHeight,
|
||||
};
|
||||
char[] pixels = mem::new_array(char, (usz)img.width * img.height);
|
||||
img.pixels = pixels;
|
||||
if (schrift::render(&font.sft, gid, img) < 0) {
|
||||
return RENDER_ERROR?;
|
||||
}
|
||||
|
||||
glyph.code = code;
|
||||
glyph.w = (ushort)img.width;
|
||||
glyph.h = (ushort)img.height;
|
||||
glyph.ox = (short)gmtx.leftSideBearing;
|
||||
glyph.oy = (short)gmtx.yOffset;
|
||||
glyph.adv = (short)gmtx.advanceWidth;
|
||||
//io::printfn("code=%c, w=%d, h=%d, ox=%d, oy=%d, adv=%d",
|
||||
// glyph.code, glyph.w, glyph.h, glyph.ox, glyph.oy, glyph.adv);
|
||||
|
||||
Point uv = font.atlas.place(pixels, glyph.w, glyph.h, (ushort)img.width)!;
|
||||
glyph.u = uv.x;
|
||||
glyph.v = uv.y;
|
||||
|
||||
mem::free(pixels);
|
||||
|
||||
font.table.set(code, glyph);
|
||||
|
||||
font.should_update = true;
|
||||
return font.table.get_ref(code);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] font
|
||||
*>
|
||||
fn void Font.free(&font)
|
||||
{
|
||||
font.atlas.free();
|
||||
font.table.free();
|
||||
schrift::freefont(font.sft.font);
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// FONT LOAD AND QUERY //
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
|
||||
module ugui;
|
||||
|
||||
<*
|
||||
@param [&inout] ctx
|
||||
@param [in] name
|
||||
@param [&in] path
|
||||
@require height > 0, scale > 0: "height and scale must be positive non-zero"
|
||||
*>
|
||||
fn void? Ctx.load_font(&ctx, Allocator allocator, String name, ZString path, uint height, float scale = 1.0)
|
||||
{
|
||||
return ctx.font.load(allocator, name, path, height, scale);
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ctx
|
||||
@param [in] label
|
||||
*>
|
||||
// TODO: check if the font is present in the context
|
||||
fn Id Ctx.get_font_id(&ctx, String label) => (Id)label.hash();
|
||||
|
||||
<*
|
||||
@param [&in] ctx
|
||||
@param [in] name
|
||||
*>
|
||||
fn Atlas*? Ctx.get_font_atlas(&ctx, String name)
|
||||
{
|
||||
// TODO: use the font name, for now there is only one font
|
||||
if (name.hash() != ctx.font.id) {
|
||||
return WRONG_ID?;
|
||||
}
|
||||
|
||||
return &ctx.font.atlas;
|
||||
}
|
||||
|
||||
<* @param [&in] font *>
|
||||
fn int Font.line_height(&font) => (int)(font.ascender - font.descender + (float)0.5);
|
||||
Loading…
Add table
Add a link
Reference in a new issue