Compare commits
3 Commits
f86a360f39
...
61556d0a2c
Author | SHA1 | Date | |
---|---|---|---|
61556d0a2c | |||
2356d165fe | |||
dbe70eb4f4 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
*.o
|
*.o
|
||||||
build/*
|
build/*
|
||||||
|
**/.ccls-cache
|
||||||
|
18
TODO
18
TODO
@ -1,7 +1,21 @@
|
|||||||
# TODOs, semi-random sorting
|
# TODOs, semi-random sorting
|
||||||
[ ] Implement glyph draw command
|
[x] Implement glyph draw command
|
||||||
[ ] Implement div.view and scrollbars
|
[x] Implement div.view and scrollbars
|
||||||
[ ] Port font system from C to C3 (rewrite1)
|
[ ] Port font system from C to C3 (rewrite1)
|
||||||
[ ] Update ARCHITECTURE.md
|
[ ] Update ARCHITECTURE.md
|
||||||
[ ] Write a README.md
|
[ ] Write a README.md
|
||||||
[ ] Use an arena allocator for cache
|
[ ] Use an arena allocator for cache
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
[ ] rect commads should have:
|
||||||
|
* border width
|
||||||
|
* border radius
|
||||||
|
[x] add a command to update an atlas
|
||||||
|
|
||||||
|
## Atlases
|
||||||
|
[ ] Add an interface to create, destroy, update and get atlases based on their ids
|
||||||
|
[ ] Implement multiple font atlases
|
||||||
|
|
||||||
|
## Fonts
|
||||||
|
[ ] Fix the missing alpha channel
|
||||||
|
[ ] Fix the allignment
|
||||||
|
3
lib/libschrift.c3l/Makefile
Normal file
3
lib/libschrift.c3l/Makefile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
all:
|
||||||
|
make -C thirdparty/libschrift
|
||||||
|
cp thirdparty/libschrift/libschrift.a linux-x64/libschrift.a
|
58
lib/libschrift.c3l/libschrift.c3
Normal file
58
lib/libschrift.c3l/libschrift.c3
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
module schrift;
|
||||||
|
|
||||||
|
def SftFont = void*;
|
||||||
|
def SftUChar = uint;
|
||||||
|
def SftGlyph = uint;
|
||||||
|
|
||||||
|
const int SFT_DOWNWARD_Y = 0x01;
|
||||||
|
|
||||||
|
struct Sft
|
||||||
|
{
|
||||||
|
SftFont font;
|
||||||
|
double xScale;
|
||||||
|
double yScale;
|
||||||
|
double xOffset;
|
||||||
|
double yOffset;
|
||||||
|
int flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SftLMetrics
|
||||||
|
{
|
||||||
|
double ascender;
|
||||||
|
double descender;
|
||||||
|
double lineGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SftGMetrics
|
||||||
|
{
|
||||||
|
double advanceWidth;
|
||||||
|
double leftSideBearing;
|
||||||
|
int yOffset;
|
||||||
|
int minWidth;
|
||||||
|
int minHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SftKerning
|
||||||
|
{
|
||||||
|
double xShift;
|
||||||
|
double yShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SftImage
|
||||||
|
{
|
||||||
|
void *pixels;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn char* sft_version() @extern("sft_version");
|
||||||
|
|
||||||
|
extern fn SftFont loadmem(void* mem, usz size) @extern("sft_loadmem");
|
||||||
|
extern fn SftFont loadfile(char* filename) @extern("sft_loadfile");
|
||||||
|
extern fn void freefont(SftFont font) @extern("sft_freefont");
|
||||||
|
|
||||||
|
extern fn int lmetrics(Sft* sft, SftLMetrics* metrics) @extern("sft_lmetrics");
|
||||||
|
extern fn int lookup(Sft* sft, SftUChar codepoint, SftGlyph* glyph) @extern("sft_lookup");
|
||||||
|
extern fn int gmetrics(Sft* sft, SftGlyph glyph, SftGMetrics* metrics) @extern("sft_gmetrics");
|
||||||
|
extern fn int kerning(Sft* sft, SftGlyph leftGlyph, SftGlyph rightGlyph, SftKerning* kerning) @extern("sft_kerning");
|
||||||
|
extern fn int render(Sft* sft, SftGlyph glyph, SftImage image) @extern("sft_render");
|
BIN
lib/libschrift.c3l/linux-x64/libschrift.a
Normal file
BIN
lib/libschrift.c3l/linux-x64/libschrift.a
Normal file
Binary file not shown.
9
lib/libschrift.c3l/manifest.json
Normal file
9
lib/libschrift.c3l/manifest.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"provides" : "schrift",
|
||||||
|
"targets" : {
|
||||||
|
"linux-x64" : {
|
||||||
|
"dependencies" : [],
|
||||||
|
"linked-libraries" : ["schrift", "c"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
lib/libschrift.c3l/project.json
Normal file
45
lib/libschrift.c3l/project.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
// Language version of C3.
|
||||||
|
"langrev": "1",
|
||||||
|
// Warnings used for all targets.
|
||||||
|
"warnings": [ "no-unused" ],
|
||||||
|
// Directories where C3 library files may be found.
|
||||||
|
"dependency-search-paths": [ ".." ],
|
||||||
|
// Libraries to use for all targets.
|
||||||
|
"dependencies": [ "schrift" ],
|
||||||
|
// Authors, optionally with email.
|
||||||
|
"authors": [ "Alessandro Mauri <alemauri001@gmail.com" ],
|
||||||
|
// Version using semantic versioning.
|
||||||
|
"version": "0.1.0",
|
||||||
|
// Sources compiled for all targets.
|
||||||
|
"sources": [ ],
|
||||||
|
// C sources if the project also compiles C sources
|
||||||
|
// relative to the project file.
|
||||||
|
// "c-sources": [ "csource/**" ],
|
||||||
|
// Output location, relative to project file.
|
||||||
|
"output": "build",
|
||||||
|
// Architecture and OS target.
|
||||||
|
// You can use 'c3c --list-targets' to list all valid targets.
|
||||||
|
// "target": "windows-x64",
|
||||||
|
"features": [
|
||||||
|
// See rcore.c3
|
||||||
|
//"SUPPORT_INTERNAL_MEMORY_MANAGEMENT",
|
||||||
|
//"SUPPORT_STANDARD_FILEIO",
|
||||||
|
//"SUPPORT_FILE_SYSTEM_FUNCTIONS",
|
||||||
|
//"SUPPORT_DATA_ENCODER",
|
||||||
|
// See text.c3
|
||||||
|
//"SUPPORT_TEXT_CODEPOINTS_MANAGEMENT",
|
||||||
|
//"SUPPORT_TEXT_C_STRING_MANAGEMENT",
|
||||||
|
//"SUPPORT_RANDOM_GENERATION",
|
||||||
|
|
||||||
|
//"SUPPORT_RAYGUI",
|
||||||
|
//"RAYGUI_NO_ICONS",
|
||||||
|
//"RAYGUI_CUSTOM_ICONS",
|
||||||
|
],
|
||||||
|
// Global settings.
|
||||||
|
// CPU name, used for optimizations in the LLVM backend.
|
||||||
|
"cpu": "generic",
|
||||||
|
// Optimization: "O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz".
|
||||||
|
"opt": "O0",
|
||||||
|
// See resources/examples/project_all_settings.json and 'c3c --list-project-properties' to see more properties.
|
||||||
|
}
|
1
lib/libschrift.c3l/thirdparty/libschrift
vendored
Submodule
1
lib/libschrift.c3l/thirdparty/libschrift
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 24737d2922b23df4a5692014f5ba03da0c296112
|
@ -6,7 +6,7 @@
|
|||||||
// Directories where C3 library files may be found.
|
// Directories where C3 library files may be found.
|
||||||
"dependency-search-paths": [ "lib" ],
|
"dependency-search-paths": [ "lib" ],
|
||||||
// Libraries to use for all targets.
|
// Libraries to use for all targets.
|
||||||
"dependencies": [ "raylib" ],
|
"dependencies": [ "raylib", "schrift" ],
|
||||||
"features": [
|
"features": [
|
||||||
// See rcore.c3
|
// See rcore.c3
|
||||||
//"SUPPORT_INTERNAL_MEMORY_MANAGEMENT",
|
//"SUPPORT_INTERNAL_MEMORY_MANAGEMENT",
|
||||||
|
40
src/main.c3
40
src/main.c3
@ -27,6 +27,7 @@ fn int main(String[] args)
|
|||||||
{
|
{
|
||||||
ugui::Ctx ui;
|
ugui::Ctx ui;
|
||||||
ui.init()!!;
|
ui.init()!!;
|
||||||
|
ui.font.load("/usr/share/fonts/TTF/FreeSans.ttf", 16, scale: 1.5)!!;
|
||||||
|
|
||||||
short width = 800;
|
short width = 800;
|
||||||
short height = 450;
|
short height = 450;
|
||||||
@ -92,6 +93,8 @@ fn int main(String[] args)
|
|||||||
ugui::Elem* e = ui.get_elem_by_label("slider")!!;
|
ugui::Elem* e = ui.get_elem_by_label("slider")!!;
|
||||||
io::printfn("slider: %f", e.slider.value);
|
io::printfn("slider: %f", e.slider.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.text_unbounded("text1", "Ciao Mamma")!!;
|
||||||
|};
|
|};
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
@ -125,6 +128,8 @@ fn int main(String[] args)
|
|||||||
// ClearBackground(BLACK);
|
// ClearBackground(BLACK);
|
||||||
|
|
||||||
rl::Color c;
|
rl::Color c;
|
||||||
|
static rl::Image font_atlas;
|
||||||
|
static rl::Texture2D font_texture;
|
||||||
for (Cmd* cmd; (cmd = ui.cmd_queue.dequeue() ?? null) != null;) {
|
for (Cmd* cmd; (cmd = ui.cmd_queue.dequeue() ?? null) != null;) {
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case ugui::CmdType.CMD_RECT:
|
case ugui::CmdType.CMD_RECT:
|
||||||
@ -141,8 +146,40 @@ fn int main(String[] args)
|
|||||||
cmd.rect.rect.h,
|
cmd.rect.rect.h,
|
||||||
c
|
c
|
||||||
);
|
);
|
||||||
|
case ugui::CmdType.CMD_UPDATE_ATLAS:
|
||||||
|
rl::unload_image(font_atlas);
|
||||||
|
font_atlas.data = cmd.update_atlas.raw_buffer;
|
||||||
|
font_atlas.width = cmd.update_atlas.width;
|
||||||
|
font_atlas.height = cmd.update_atlas.height;
|
||||||
|
font_atlas.mipmaps = 1;
|
||||||
|
//font_atlas.format = rl::PixelFormat.PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
|
||||||
|
font_atlas.format = 1;
|
||||||
|
if (rl::is_texture_ready(font_texture)) {
|
||||||
|
rl::unload_texture(font_texture);
|
||||||
|
}
|
||||||
|
font_texture = rl::load_texture_from_image(font_atlas);
|
||||||
|
//rl::draw_texture(font_texture, 0, 0, rl::WHITE);
|
||||||
|
case ugui::CmdType.CMD_SPRITE:
|
||||||
|
rl::Rectangle source = {
|
||||||
|
.x = cmd.sprite.texture_rect.x,
|
||||||
|
.y = cmd.sprite.texture_rect.y,
|
||||||
|
.width = cmd.sprite.texture_rect.w,
|
||||||
|
.height = cmd.sprite.texture_rect.h,
|
||||||
|
};
|
||||||
|
rl::Vector2 position = {
|
||||||
|
.x = cmd.sprite.rect.x,
|
||||||
|
.y = cmd.sprite.rect.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
rl::draw_texture_rec(font_texture, source, position, rl::WHITE);
|
||||||
|
//rl::draw_rectangle(cmd.sprite.rect.x,
|
||||||
|
// cmd.sprite.rect.y,
|
||||||
|
// cmd.sprite.rect.w,
|
||||||
|
// cmd.sprite.rect.h,
|
||||||
|
// rl::WHITE
|
||||||
|
//);
|
||||||
default:
|
default:
|
||||||
io::printfn("Unknown cmd type: %d", cmd.type);
|
io::printfn("Unknown cmd type: %s", cmd.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
draw_times.push(clock.mark());
|
draw_times.push(clock.mark());
|
||||||
@ -154,6 +191,7 @@ fn int main(String[] args)
|
|||||||
|
|
||||||
rl::close_window();
|
rl::close_window();
|
||||||
|
|
||||||
|
ui.font.free();
|
||||||
ui.free();
|
ui.free();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ enum ElemType {
|
|||||||
ETYPE_DIV,
|
ETYPE_DIV,
|
||||||
ETYPE_BUTTON,
|
ETYPE_BUTTON,
|
||||||
ETYPE_SLIDER,
|
ETYPE_SLIDER,
|
||||||
|
ETYPE_TEXT,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitstruct ElemFlags : uint {
|
bitstruct ElemFlags : uint {
|
||||||
@ -75,6 +76,10 @@ struct Slider {
|
|||||||
Rect handle;
|
Rect handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Text {
|
||||||
|
char* str;
|
||||||
|
}
|
||||||
|
|
||||||
// element structure
|
// element structure
|
||||||
struct Elem {
|
struct Elem {
|
||||||
Id id;
|
Id id;
|
||||||
@ -85,6 +90,7 @@ struct Elem {
|
|||||||
union {
|
union {
|
||||||
Div div;
|
Div div;
|
||||||
Slider slider;
|
Slider slider;
|
||||||
|
Text text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +132,8 @@ const uint ROOT_ID = 1;
|
|||||||
// command type
|
// command type
|
||||||
enum CmdType {
|
enum CmdType {
|
||||||
CMD_RECT,
|
CMD_RECT,
|
||||||
|
CMD_UPDATE_ATLAS,
|
||||||
|
CMD_SPRITE,
|
||||||
}
|
}
|
||||||
|
|
||||||
// command to draw a rect
|
// command to draw a rect
|
||||||
@ -134,11 +142,27 @@ struct CmdRect {
|
|||||||
Color color;
|
Color color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: For now only support black and white atlas, so PIXELFORMAT_UNCOMPRESSED_GRAYSCALE
|
||||||
|
struct CmdUpdateAtlas {
|
||||||
|
char* raw_buffer;
|
||||||
|
short width, height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 1. Add atlases as a data type
|
||||||
|
// 2. Each atlas has an id
|
||||||
|
struct CmdSprite {
|
||||||
|
Rect rect;
|
||||||
|
Rect texture_rect;
|
||||||
|
}
|
||||||
|
|
||||||
// command structure
|
// command structure
|
||||||
struct Cmd {
|
struct Cmd {
|
||||||
CmdType type;
|
CmdType type;
|
||||||
union {
|
union {
|
||||||
CmdRect rect;
|
CmdRect rect;
|
||||||
|
CmdUpdateAtlas update_atlas;
|
||||||
|
CmdSprite sprite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +191,7 @@ struct Ctx {
|
|||||||
// total size in pixels of the context
|
// total size in pixels of the context
|
||||||
ushort width, height;
|
ushort width, height;
|
||||||
Style style;
|
Style style;
|
||||||
|
Font font;
|
||||||
|
|
||||||
bool has_focus;
|
bool has_focus;
|
||||||
struct input {
|
struct input {
|
||||||
|
221
src/ugui_font.c3
Normal file
221
src/ugui_font.c3
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
module ugui;
|
||||||
|
|
||||||
|
import schrift;
|
||||||
|
import std::collections::map;
|
||||||
|
import std::core::mem;
|
||||||
|
import std::io;
|
||||||
|
|
||||||
|
// 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 | | |
|
||||||
|
* | :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;
|
||||||
|
short idx; // atlas index
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint FONT_CACHED = 512;
|
||||||
|
def GlyphTable = map::HashMap(<Codepoint, Glyph>) @private;
|
||||||
|
|
||||||
|
fault UgFontError {
|
||||||
|
TTF_LOAD_FAILED,
|
||||||
|
MISSING_GLYPH,
|
||||||
|
BAD_GLYPH_METRICS,
|
||||||
|
RENDER_ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
|
fault UgAtlasError {
|
||||||
|
CANNOT_PLACE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// black and white atlas
|
||||||
|
struct AtlasBW {
|
||||||
|
ushort width, height;
|
||||||
|
char[] buffer;
|
||||||
|
|
||||||
|
Point row;
|
||||||
|
ushort row_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Font {
|
||||||
|
schrift::Sft sft;
|
||||||
|
String path;
|
||||||
|
GlyphTable table;
|
||||||
|
|
||||||
|
float size;
|
||||||
|
float ascender, descender, linegap; // Line Metrics
|
||||||
|
AtlasBW[] atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! AtlasBW.new(&atlas, ushort width, ushort height)
|
||||||
|
{
|
||||||
|
atlas.width = width;
|
||||||
|
atlas.height = height;
|
||||||
|
atlas.buffer = mem::new_array(char, (usz)atlas.width*atlas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void AtlasBW.free(&atlas)
|
||||||
|
{
|
||||||
|
free(atlas.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// place a rect inside the atlas
|
||||||
|
// uses a row first algorithm
|
||||||
|
// TODO: use a skyline algorithm https://jvernay.fr/en/blog/skyline-2d-packer/implementation/
|
||||||
|
fn Point! AtlasBW.place(&atlas, char[] pixels, ushort w, ushort h)
|
||||||
|
{
|
||||||
|
Point p;
|
||||||
|
|
||||||
|
if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) {
|
||||||
|
p = atlas.row;
|
||||||
|
} else {
|
||||||
|
atlas.row.x = 0;
|
||||||
|
atlas.row.y = atlas.row.y + atlas.row_h;
|
||||||
|
atlas.row_h = 0;
|
||||||
|
if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) {
|
||||||
|
p = atlas.row;
|
||||||
|
} else {
|
||||||
|
return UgAtlasError.CANNOT_PLACE?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usz y = 0; y < h; y++) {
|
||||||
|
for (usz x = 0; x < w; x++) {
|
||||||
|
atlas.buffer[(usz)(p.y+y)*atlas.width + (p.x+x)] = pixels[y*w + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atlas.row.x += w;
|
||||||
|
if (h > atlas.row_h) {
|
||||||
|
atlas.row_h = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void! Font.load(&font, String path, uint height, float scale = 1)
|
||||||
|
{
|
||||||
|
font.table.new_init(capacity: FONT_CACHED);
|
||||||
|
|
||||||
|
font.size = height*scale;
|
||||||
|
|
||||||
|
font.sft = schrift::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) {
|
||||||
|
return UgFontError.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'
|
||||||
|
font.atlas = mem::new_array(AtlasBW, 1);
|
||||||
|
ushort size = (ushort)font.size*256;
|
||||||
|
font.atlas[0].new(size, size)!;
|
||||||
|
|
||||||
|
// preallocate the ASCII range
|
||||||
|
// for (char c = ' '; c < '~'; c++) {
|
||||||
|
// font.get_glyph((Codepoint)c)!;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Glyph*! Font.get_glyph(&font, Codepoint code, bool* is_new = null)
|
||||||
|
{
|
||||||
|
Glyph*! gp;
|
||||||
|
gp = font.table.get_ref(code);
|
||||||
|
|
||||||
|
if (catch excuse = gp) {
|
||||||
|
if (excuse != SearchResult.MISSING) {
|
||||||
|
return excuse?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_new) { *is_new = false; }
|
||||||
|
return gp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing glyph, render and place into an atlas
|
||||||
|
Glyph glyph;
|
||||||
|
|
||||||
|
schrift::SftGlyph gid;
|
||||||
|
schrift::SftGMetrics gmtx;
|
||||||
|
|
||||||
|
if (schrift::lookup(&font.sft, code, &gid) < 0) {
|
||||||
|
return UgFontError.MISSING_GLYPH?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schrift::gmetrics(&font.sft, gid, &gmtx) < 0) {
|
||||||
|
return UgFontError.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 UgFontError.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[0].place(pixels, glyph.w, glyph.h)!;
|
||||||
|
glyph.idx = 0;
|
||||||
|
glyph.u = uv.x;
|
||||||
|
glyph.v = uv.y;
|
||||||
|
|
||||||
|
mem::free(pixels);
|
||||||
|
|
||||||
|
font.table.set(code, glyph);
|
||||||
|
|
||||||
|
if (is_new) { *is_new = true; }
|
||||||
|
return font.table.get_ref(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Font.free(&font)
|
||||||
|
{
|
||||||
|
foreach (atlas: font.atlas) {
|
||||||
|
atlas.free();
|
||||||
|
}
|
||||||
|
schrift::freefont(font.sft.font);
|
||||||
|
}
|
83
src/ugui_text.c3
Normal file
83
src/ugui_text.c3
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
module ugui;
|
||||||
|
|
||||||
|
import std::io;
|
||||||
|
|
||||||
|
fn void! Ctx.text_unbounded(&ctx, String label, String text)
|
||||||
|
{
|
||||||
|
Id id = label.hash();
|
||||||
|
|
||||||
|
Elem *parent = ctx.get_parent()!;
|
||||||
|
Elem *c_elem = ctx.get_elem(id)!;
|
||||||
|
// add it to the tree
|
||||||
|
ctx.tree.add(id, ctx.active_div)!;
|
||||||
|
|
||||||
|
// 1. Fill the element fields
|
||||||
|
// this resets the flags
|
||||||
|
c_elem.type = ETYPE_TEXT;
|
||||||
|
|
||||||
|
short line_height = (short)ctx.font.ascender - (short)ctx.font.descender;
|
||||||
|
short baseline = (short)ctx.font.ascender;
|
||||||
|
bool update_atlas;
|
||||||
|
// if the element is new or the parent was updated then redo layout
|
||||||
|
if (c_elem.flags.is_new || parent.flags.updated) {
|
||||||
|
Rect text_size;
|
||||||
|
Glyph* gp;
|
||||||
|
|
||||||
|
// FIXME: newlines are not counted
|
||||||
|
foreach (c: text) {
|
||||||
|
Codepoint cp = (Codepoint)c;
|
||||||
|
bool n;
|
||||||
|
gp = ctx.font.get_glyph(cp, &n)!;
|
||||||
|
text_size.w += gp.adv;
|
||||||
|
text_size.h += line_height;
|
||||||
|
if (n) { update_atlas = true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Layout
|
||||||
|
c_elem.bounds = ctx.position_element(parent, text_size, true);
|
||||||
|
|
||||||
|
// 3. Fill the button specific fields
|
||||||
|
c_elem.text.str = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_atlas) {
|
||||||
|
// FIXME: atlas here is hardcoded, look at the todo in ugui_data
|
||||||
|
Cmd up = {
|
||||||
|
.type = CMD_UPDATE_ATLAS,
|
||||||
|
.update_atlas = {
|
||||||
|
.raw_buffer = ctx.font.atlas[0].buffer,
|
||||||
|
.width = ctx.font.atlas[0].width,
|
||||||
|
.height = ctx.font.atlas[0].height,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ctx.cmd_queue.enqueue(&up)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point orig = {
|
||||||
|
.x = c_elem.bounds.x,
|
||||||
|
.y = c_elem.bounds.y,
|
||||||
|
};
|
||||||
|
foreach (c: text) {
|
||||||
|
Glyph* gp;
|
||||||
|
Codepoint cp = (Codepoint)c;
|
||||||
|
gp = ctx.font.get_glyph(cp)!;
|
||||||
|
|
||||||
|
Cmd cmd = {
|
||||||
|
.type = CMD_SPRITE,
|
||||||
|
.sprite.rect = {
|
||||||
|
.x = orig.x + gp.ox,
|
||||||
|
.y = orig.y + gp.oy + baseline,
|
||||||
|
.w = gp.w,
|
||||||
|
.h = gp.h,
|
||||||
|
},
|
||||||
|
.sprite.texture_rect = {
|
||||||
|
.x = gp.u,
|
||||||
|
.y = gp.v,
|
||||||
|
.w = gp.w,
|
||||||
|
.h = gp.h,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
orig.x += gp.adv;
|
||||||
|
ctx.cmd_queue.enqueue(&cmd)!;
|
||||||
|
}
|
||||||
|
}
|
6
test/test_font.c3
Normal file
6
test/test_font.c3
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import rl;
|
||||||
|
|
||||||
|
fn int main(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user