From 34ab8b9355326eb2a81f9513d78b09152246adec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Ma=C5=A1karinec?= Date: Sun, 14 Jul 2024 22:14:39 +0200 Subject: [PATCH] Add generic font interface + sample (#192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marek Maškarinec --- lib/miniaudio | 2 +- lib/sokol | 2 +- samples/font/combined/main.um | 33 +++++++++ samples/font/ttf/main.um | 25 +++++++ src/bindings.c | 18 ++--- src/staembed.c | 127 ++++++++++++++++++++-------------- umka/canvas.um | 26 ++++++- umka/font.um | 56 ++++++++------- 8 files changed, 200 insertions(+), 89 deletions(-) create mode 100644 samples/font/combined/main.um create mode 100644 samples/font/ttf/main.um diff --git a/lib/miniaudio b/lib/miniaudio index b19cc09..4a5b74b 160000 --- a/lib/miniaudio +++ b/lib/miniaudio @@ -1 +1 @@ -Subproject commit b19cc09fd06b80f370ca4385d260df7e31925b50 +Subproject commit 4a5b74bef029b3592c54b6048650ee5f972c1a48 diff --git a/lib/sokol b/lib/sokol index b1221d1..7b20c19 160000 --- a/lib/sokol +++ b/lib/sokol @@ -1 +1 @@ -Subproject commit b1221d1f2bfc3cbb22f566cfa8f31b4073c287ae +Subproject commit 7b20c1936229370277d1c61bde950bce194de584 diff --git a/samples/font/combined/main.um b/samples/font/combined/main.um new file mode 100644 index 0000000..ff8f0f6 --- /dev/null +++ b/samples/font/combined/main.um @@ -0,0 +1,33 @@ + +import ( + "canvas.um" + "font.um" + "input.um" + "std.um" + "th.um" + "window.um" +) + +var fonts: [2]font::Font +var idx: int + +fn init*() { + window::setup("combined font sample", 640, 480) + + var err: std::Err + fonts[0], err = font::load("../../../etc/roboto.ttf", 16, .nearest) + std::exitif(err) + + fonts[1] = canvas::pixelFont + + window::onFrame.register({ + const scale = 2.0 + m := fonts[idx].measure("Press SPACE").mulf(scale) + pos := window::wp.sub(m).divf(2) + fonts[idx].draw("Press SPACE", pos, th::black, scale) + + if input::isJustPressed(.space) { + idx = (idx + 1) % len(fonts) + } + }) +} diff --git a/samples/font/ttf/main.um b/samples/font/ttf/main.um new file mode 100644 index 0000000..14a4cac --- /dev/null +++ b/samples/font/ttf/main.um @@ -0,0 +1,25 @@ + +import ( + "canvas.um" + "th.um" + "window.um" + "std.um" + "font.um" +) + +var f: font::Font + +fn init*() { + window::setup("font sample", 640, 480) + + var err: std::Err + f, err = font::load("../../../etc/roboto.ttf", 16, .nearest) + std::exitif(err) + + window::onFrame.register({ + const scale = 2.0 + m := f.measure("Hello World!").mulf(scale) + pos := window::wp.sub(m).divf(2) + f.draw("Hello World!", pos, th::black, scale) + }) +} diff --git a/src/bindings.c b/src/bindings.c index 4cd0965..d02d6be 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -120,9 +120,9 @@ umth_placeholder_fetch(UmkaStackSlot *p, UmkaStackSlot *r) /////////////////////////// // FONT -// fn umth_font_load(out: ^Font, path: str, size: real, filter: uint32): th::ErrCode +// fn umth_ttf_font_load(out: ^TtfFont, path: str, size: real, filter: uint32): th::ErrCode static void -umth_font_load(UmkaStackSlot *p, UmkaStackSlot *r) +umth_ttf_font_load(UmkaStackSlot *p, UmkaStackSlot *r) { th_font **ft = umkaGetParam(p, 0)->ptrVal; char *path = conv_path(umkaGetParam(p, 1)->ptrVal); @@ -133,9 +133,9 @@ umth_font_load(UmkaStackSlot *p, UmkaStackSlot *r) free(path); } -// fn umth_font_draw(font: Font, s: str, x: real, y: real, color: uint32, scale: real) +// fn umth_ttf_font_draw(font: TtfFont, s: str, x: real, y: real, color: uint32, scale: real) static void -umth_font_draw(UmkaStackSlot *p, UmkaStackSlot *r) +umth_ttf_font_draw(UmkaStackSlot *p, UmkaStackSlot *r) { th_font *font = umkaGetParam(p, 0)->ptrVal; const char *s = umkaGetParam(p, 1)->ptrVal; @@ -147,9 +147,9 @@ umth_font_draw(UmkaStackSlot *p, UmkaStackSlot *r) th_font_draw(font, s, x, y, color, scale); } -// fn umth_font_measure(font: Font, s: str): th::Vf2 +// fn umth_ttf_font_measure(font: TtfFont, s: str): th::Vf2 static void -umth_font_measure(UmkaStackSlot *p, UmkaStackSlot *r) +umth_ttf_font_measure(UmkaStackSlot *p, UmkaStackSlot *r) { th_font *font = umkaGetParam(p, 0)->ptrVal; const char *s = umkaGetParam(p, 1)->ptrVal; @@ -1441,9 +1441,9 @@ _th_umka_bind(void *umka) umkaAddFunc(umka, "umth_rgb_uint32", &umth_rgb_uint32); // font - umkaAddFunc(umka, "umth_font_load", &umth_font_load); - umkaAddFunc(umka, "umth_font_draw", &umth_font_draw); - umkaAddFunc(umka, "umth_font_measure", &umth_font_measure); + umkaAddFunc(umka, "umth_ttf_font_load", &umth_ttf_font_load); + umkaAddFunc(umka, "umth_ttf_font_draw", &umth_ttf_font_draw); + umkaAddFunc(umka, "umth_ttf_font_measure", &umth_ttf_font_measure); // particles umkaAddFunc(umka, "umth_particles_draw", &umth_particles_draw); diff --git a/src/staembed.c b/src/staembed.c index 129fd7c..5a53bfd 100644 --- a/src/staembed.c +++ b/src/staembed.c @@ -1,4 +1,4 @@ -#include "tophat.h" +#include "tophat.h" const char *th_em_modulesrc[] = { "\n" "import (\n" @@ -1218,6 +1218,7 @@ const char *th_em_modulesrc[] = { "//~~\n" "\n" "import (\n" +"\t\"font.um\"\n" "\t\"misc.um\"\n" "\t\"rect.um\"\n" "\t\"th.um\"\n" @@ -1261,6 +1262,25 @@ const char *th_em_modulesrc[] = { "\treturn {maxw * scale * 6 - scale, h * 6 * scale}\n" "}\n" "\n" +"type __PixelFont = struct { }\n" +"\n" +"fn (p: ^__PixelFont) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) {\n" +"\tdrawText(text, pos, color, scale)\n" +"}\n" +"\n" +"fn (p: ^__PixelFont) measure*(text: str): th::Vf2 {\n" +"\treturn textSize(text, 1)\n" +"}\n" +"\n" +"fn (p: ^__PixelFont) validate*(): bool {\n" +"\treturn true\n" +"}\n" +"\n" +"//~~Pixel Font\n" +"// The `pixelFont` variable exposes the canvas pixel font as a generic font.\n" +"var pixelFont*: __PixelFont\n" +"//~~\n" +"\n" "fn umth_canvas_draw_rect(color: uint32, r: rect::Rect)\n" "//~~fn drawRect\n" "// Draws a Rectangle.\n" @@ -1302,7 +1322,7 @@ const char *th_em_modulesrc[] = { "\tx := misc::maxf(a.x, b.x);\n" "\ty := misc::maxf(a.y, b.y);\n" "\treturn rect::Rect{\n" -"\t\tx,y, \n" +"\t\tx,y,\n" "\t\tmisc::minf(a.x + a.w, b.x + b.w) - x,\n" "\t\tmisc::minf(a.y + a.h, b.y + b.h) - y};\n" "}\n" @@ -1321,9 +1341,9 @@ const char *th_em_modulesrc[] = { "\t}\n" "\n" "\tif debug {\n" -"\t\tdrawRectLines(th::red, r2, 0.1) \n" +"\t\tdrawRectLines(th::red, r2, 0.1)\n" "\t}\n" -"\t\n" +"\n" "\tumth_canvas_begin_scissor_rect(r2)\n" "}\n" "\n" @@ -2029,45 +2049,53 @@ const char *th_em_modulesrc[] = { "}\n" "//~~\n" "\n" -"//~~opaque Font\n" -"type Font* = struct { _: ^struct{} }\n" -"//~~\n" +"// TODO: should this be exported and documented?\n" +"type TtfFont* = struct { _: ^struct{} }\n" "\n" -"fn umth_font_load(f: ^Font, path: str, size: th::fu, filter: Filter): th::ErrCode\n" -"//~~fn load\n" -"fn load*(path: str, size: th::fu, filter: Filter = Filter.linear): (Font, std::Err) {\n" -"//~~\n" -"\tvar f: Font\n" -"\tec := umth_font_load(&f, path, size, filter)\n" -"\treturn f, th::__errFromCode(ec)\n" -"}\n" +"fn umth_ttf_font_load(f: ^TtfFont, path: str, size: th::fu, filter: Filter): th::ErrCode\n" +"fn umth_ttf_font_draw(font: TtfFont, s: str, x: th::fu, y: th::fu, color: uint32, scale: th::fu)\n" +"fn umth_ttf_font_measure(font: TtfFont, s: str): th::Vf2\n" "\n" -"//~~fn Font.validate\n" -"fn (f: ^Font) validate*(): bool {\n" -"//~~\n" +"fn (f: ^TtfFont) validate*(): bool {\n" "\treturn f._ != null\n" "}\n" "\n" -"fn umth_font_draw(font: Font, s: str, x: th::fu, y: th::fu, color: uint32, scale: th::fu)\n" -"//~~fn Font.draw\n" -"fn (f: ^Font) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) {\n" -"//~~\n" +"fn (f: ^TtfFont) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) {\n" "\tif !f.validate() {\n" "\t\tth::__error(\"Invalid font\")\n" "\t}\n" -"\tumth_font_draw(f^, text, pos.x, pos.y, color, scale)\n" +"\tumth_ttf_font_draw(f^, text, pos.x, pos.y, color, scale)\n" "}\n" "\n" -"fn umth_font_measure(font: Font, s: str): th::Vf2\n" -"//~~fn Font.measure\n" -"fn (f: ^Font) measure*(text: str): th::Vf2 {\n" -"//~~\n" +"fn (f: ^TtfFont) measure*(text: str): th::Vf2 {\n" "\tif !f.validate() {\n" "\t\tth::__error(\"Invalid font\")\n" "\t\treturn {}\n" "\t}\n" "\n" -"\treturn umth_font_measure(f^, text)\n" +"\treturn umth_ttf_font_measure(f^, text)\n" +"}\n" +"\n" +"//~~interface Font\n" +"// A generic interface for fonts. Fonts can be loaded from file using\n" +"// `font.load`, or a built in canvas can be used from `canvas.pixelFont`.\n" +"type Font* = interface {\n" +"\t// Draw text to the viewport\n" +"\tdraw(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0)\n" +"\t// Return the dimensions of the text at scale 1\n" +"\tmeasure(text: str): th::Vf2\n" +"\t// Return true if the font is valid\n" +"\tvalidate(): bool\n" +"}\n" +"//~~\n" +"\n" +"//~~fn load\n" +"// Loads a font from a path and returns it.\n" +"fn load*(path: str, size: th::fu, filter: Filter = Filter.linear): (Font, std::Err) {\n" +"//~~\n" +"\tvar f: TtfFont\n" +"\tec := umth_ttf_font_load(&f, path, size, filter)\n" +"\treturn f, th::__errFromCode(ec)\n" "}\n" "", "//~~\n" @@ -4944,6 +4972,14 @@ const char *th_em_moduledocs[] = { "\n" "---------\n" "\n" +"Pixel Font\n" +"\n" +"var pixelFont*: __PixelFont\n" +"\n" +"The `pixelFont` variable exposes the canvas pixel font as a generic font.\n" +"\n" +"---------\n" +"\n" "fn drawRect\n" "\n" "fn drawRect*(color: uint32, r: rect::Rect) {\n" @@ -5501,10 +5537,19 @@ const char *th_em_moduledocs[] = { "\n" "---------\n" "\n" -"opaque Font\n" +"interface Font\n" "\n" -"type Font* = struct { _: ^struct{} }\n" +"type Font* = interface {\n" +"\t// Draw text to the viewport\n" +"\tdraw(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0)\n" +"\t// Return the dimensions of the text at scale 1\n" +"\tmeasure(text: str): th::Vf2\n" +"\t// Return true if the font is valid\n" +"\tvalidate(): bool\n" +"}\n" "\n" +"A generic interface for fonts. Fonts can be loaded from file using\n" +"`font.load`, or a built in canvas can be used from `canvas.pixelFont`.\n" "\n" "---------\n" "\n" @@ -5512,27 +5557,7 @@ const char *th_em_moduledocs[] = { "\n" "fn load*(path: str, size: th::fu, filter: Filter = Filter.linear): (Font, std::Err) {\n" "\n" -"\n" -"---------\n" -"\n" -"fn Font.validate\n" -"\n" -"fn (f: ^Font) validate*(): bool {\n" -"\n" -"\n" -"---------\n" -"\n" -"fn Font.draw\n" -"\n" -"fn (f: ^Font) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) {\n" -"\n" -"\n" -"---------\n" -"\n" -"fn Font.measure\n" -"\n" -"fn (f: ^Font) measure*(text: str): th::Vf2 {\n" -"\n" +"Loads a font from a path and returns it.\n" "\n" "---------\n" "\n" diff --git a/umka/canvas.um b/umka/canvas.um index 9a8424e..9c9d6fe 100644 --- a/umka/canvas.um +++ b/umka/canvas.um @@ -4,6 +4,7 @@ //~~ import ( + "font.um" "misc.um" "rect.um" "th.um" @@ -47,6 +48,25 @@ fn textSize*(text: str, scale: th::fu): th::Vf2 { return {maxw * scale * 6 - scale, h * 6 * scale} } +type __PixelFont = struct { } + +fn (p: ^__PixelFont) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) { + drawText(text, pos, color, scale) +} + +fn (p: ^__PixelFont) measure*(text: str): th::Vf2 { + return textSize(text, 1) +} + +fn (p: ^__PixelFont) validate*(): bool { + return true +} + +//~~Pixel Font +// The `pixelFont` variable exposes the canvas pixel font as a generic font. +var pixelFont*: __PixelFont +//~~ + fn umth_canvas_draw_rect(color: uint32, r: rect::Rect) //~~fn drawRect // Draws a Rectangle. @@ -88,7 +108,7 @@ fn rectDiff(a, b: rect::Rect): rect::Rect { x := misc::maxf(a.x, b.x); y := misc::maxf(a.y, b.y); return rect::Rect{ - x,y, + x,y, misc::minf(a.x + a.w, b.x + b.w) - x, misc::minf(a.y + a.h, b.y + b.h) - y}; } @@ -107,9 +127,9 @@ fn beginScissorRect*(r: rect::Rect, debug: bool = false) { } if debug { - drawRectLines(th::red, r2, 0.1) + drawRectLines(th::red, r2, 0.1) } - + umth_canvas_begin_scissor_rect(r2) } diff --git a/umka/font.um b/umka/font.um index 978cc87..88c43c6 100644 --- a/umka/font.um +++ b/umka/font.um @@ -14,43 +14,51 @@ type Filter* = enum { } //~~ -//~~opaque Font -type Font* = struct { _: ^struct{} } -//~~ +// TODO: should this be exported and documented? +type TtfFont* = struct { _: ^struct{} } -fn umth_font_load(f: ^Font, path: str, size: th::fu, filter: Filter): th::ErrCode -//~~fn load -fn load*(path: str, size: th::fu, filter: Filter = Filter.linear): (Font, std::Err) { -//~~ - var f: Font - ec := umth_font_load(&f, path, size, filter) - return f, th::__errFromCode(ec) -} +fn umth_ttf_font_load(f: ^TtfFont, path: str, size: th::fu, filter: Filter): th::ErrCode +fn umth_ttf_font_draw(font: TtfFont, s: str, x: th::fu, y: th::fu, color: uint32, scale: th::fu) +fn umth_ttf_font_measure(font: TtfFont, s: str): th::Vf2 -//~~fn Font.validate -fn (f: ^Font) validate*(): bool { -//~~ +fn (f: ^TtfFont) validate*(): bool { return f._ != null } -fn umth_font_draw(font: Font, s: str, x: th::fu, y: th::fu, color: uint32, scale: th::fu) -//~~fn Font.draw -fn (f: ^Font) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) { -//~~ +fn (f: ^TtfFont) draw*(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) { if !f.validate() { th::__error("Invalid font") } - umth_font_draw(f^, text, pos.x, pos.y, color, scale) + umth_ttf_font_draw(f^, text, pos.x, pos.y, color, scale) } -fn umth_font_measure(font: Font, s: str): th::Vf2 -//~~fn Font.measure -fn (f: ^Font) measure*(text: str): th::Vf2 { -//~~ +fn (f: ^TtfFont) measure*(text: str): th::Vf2 { if !f.validate() { th::__error("Invalid font") return {} } - return umth_font_measure(f^, text) + return umth_ttf_font_measure(f^, text) +} + +//~~interface Font +// A generic interface for fonts. Fonts can be loaded from file using +// `font.load`, or a built in canvas can be used from `canvas.pixelFont`. +type Font* = interface { + // Draw text to the viewport + draw(text: str, pos: th::Vf2, color: uint32, scale: th::fu = 1.0) + // Return the dimensions of the text at scale 1 + measure(text: str): th::Vf2 + // Return true if the font is valid + validate(): bool +} +//~~ + +//~~fn load +// Loads a font from a path and returns it. +fn load*(path: str, size: th::fu, filter: Filter = Filter.linear): (Font, std::Err) { +//~~ + var f: TtfFont + ec := umth_ttf_font_load(&f, path, size, filter) + return f, th::__errFromCode(ec) }