Skip to content

Commit

Permalink
Make nuklear use fontstash (and freetype) for sharper fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
rj45 committed May 15, 2024
1 parent 4e559fc commit 674c8ae
Show file tree
Hide file tree
Showing 10 changed files with 3,258 additions and 2,765 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ set(DIGILOGIC_SRCS
src/import/digital.c
src/autoroute/autoroute.c
src/render/fons_sgp.c
src/render/sokol_nuklear.c
src/render/fons_nuklear.c
src/render/polyline.c
src/render/draw.c
src/main.c
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ HEADERS = src/core/core.h src/assets.h src/view/view.h src/ux/ux.h src/shaders/a
SRCS = src/core/circuit.c src/ux/ux.c src/ux/input.c src/ux/snap.c src/ux/undo.c src/view/view.c src/import/digital.c src/autoroute/autoroute.c src/core/smap.c
THIRDPARTY = $(wildcard thirdparty/*.h)
THIRDPARTY_LIBS = thirdparty/routing/target/release/libdigilogic_routing.a
MAIN_SRCS = $(SRCS) src/main.c src/apple.m src/assets.c src/render/fons_sgp.c src/render/polyline.c src/render/draw.c src/ui/ui.c
MAIN_SRCS = $(SRCS) src/main.c src/apple.m src/assets.c src/render/fons_sgp.c src/render/sokol_nuklear.c src/render/fons_nuklear.c src/render/polyline.c src/render/draw.c src/ui/ui.c
TEST_SRCS = $(SRCS) src/test.c src/ux/ux_test.c src/view/view_test.c src/core/core_test.c src/render/draw_test.c

CFLAGS = -std=c11 -DSOKOL_METAL -I thirdparty -I src -Wall -Werror \
Expand Down
88 changes: 32 additions & 56 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@

#include <stdint.h>
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_SOFTWARE_FONT

#include "ui/ui.h"
#include <stdio.h>
Expand All @@ -41,19 +40,21 @@
#include "sokol_glue.h"
#include "sokol_gp.h"
#include "sokol_log.h"
#include "sokol_nuklear.h"
#include "sokol_time.h"
#include "stb_image.h"

#include "render/fons_sgp.h"
#include "render/sokol_nuklear.h"

#include "render/fons_nuklear.h"

#include "render/render.h"

#define LOG_LEVEL LL_DEBUG
#include "log.h"

#define FONT_ATLAS_WIDTH 1536
#define FONT_ATLAS_HEIGHT 1536
#define FONT_ATLAS_WIDTH 2048
#define FONT_ATLAS_HEIGHT 2048

#define UI_FONT_SIZE 20

Expand All @@ -69,11 +70,7 @@ typedef struct my_app_t {
FONScontext *fsctx;
FonsFont fonsFont;

struct nk_font *latin;
struct nk_font_atlas atlas;
sg_image font_img;
sg_sampler font_smp;
snk_image_t default_font;
struct nk_user_font nkFont;

DrawContext draw;

Expand All @@ -90,7 +87,24 @@ typedef struct my_app_t {
static void fons_error(void *user_ptr, int error, int val) {
my_app_t *app = (my_app_t *)user_ptr;
(void)app;
fprintf(stderr, "FONS error: %d %d\n", error, val);

switch (error) {
case FONS_ATLAS_FULL:
log_error("FONS_ATLAS_FULL: Fontstash atlas full: %d\n", val);
break;
case FONS_SCRATCH_FULL:
log_error("FONS_SCRATCH_FULL: Fontstash scratch full: %d\n", val);
break;
case FONS_STATES_OVERFLOW:
log_error("FONS_STATES_OVERFLOW: Fontstash state overflow: %d\n", val);
break;
case FONS_STATES_UNDERFLOW:
log_error("FONS_STATES_UNDERFLOW: Fonstash state underflow: %d\n", val);
break;
default:
log_error("Unknown fonstash error: %d %d\n", error, val);
break;
}
}

static void init(void *user_data) {
Expand Down Expand Up @@ -121,13 +135,6 @@ static void init(void *user_data) {

sg_enable_frame_stats();

snk_setup(&(snk_desc_t){
.max_vertices = 64 * 1024,
.logger.func = slog_func,
.sample_count = 4,
.no_default_font = true,
});

app->fsctx = fsgp_create(
&(fsgp_desc_t){.width = FONT_ATLAS_WIDTH, .height = FONT_ATLAS_HEIGHT});
if (!app->fsctx) {
Expand Down Expand Up @@ -163,42 +170,13 @@ static void init(void *user_data) {
.iconFont = iconFont,
};

struct nk_font_config cfg_latin = nk_font_config(UI_FONT_SIZE);
cfg_latin.range = nk_font_default_glyph_ranges();
cfg_latin.oversample_h = cfg_latin.oversample_v = 4;
cfg_latin.pixel_snap = false;

app->font_smp = sg_make_sampler(&(sg_sampler_desc){
.min_filter = SG_FILTER_LINEAR,
.mag_filter = SG_FILTER_LINEAR,
.wrap_u = SG_WRAP_CLAMP_TO_EDGE,
.wrap_v = SG_WRAP_CLAMP_TO_EDGE,
.label = "sokol-nuklear-font-sampler",
snk_setup(&(snk_desc_t){
.max_vertices = 64 * 1024,
.logger.func = slog_func,
.sample_count = 4,
});

nk_font_atlas_init_default(&app->atlas);
nk_font_atlas_begin(&app->atlas);
int font_width = 0, font_height = 0;
app->latin = nk_font_atlas_add_from_memory(
&app->atlas, (void *)mainFontData, mainFontSize, UI_FONT_SIZE, &cfg_latin);
const void *pixels = nk_font_atlas_bake(
&app->atlas, &font_width, &font_height, NK_FONT_ATLAS_RGBA32);
assert((font_width > 0) && (font_height > 0));
app->font_img = sg_make_image(&(sg_image_desc){
.width = font_width,
.height = font_height,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.data.subimage[0][0] =
{.ptr = pixels,
.size = (size_t)(font_width * font_height) * sizeof(uint32_t)},
.label = "sokol-nuklear-font"});
app->default_font = snk_make_image(&(snk_image_desc_t){
.image = app->font_img,
.sampler = app->font_smp,
});
nk_font_atlas_end(&app->atlas, snk_nkhandle(app->default_font), 0);
nk_font_atlas_cleanup(&app->atlas);
snk_set_atlas(&app->atlas);
nuklear_fontstash_init(&app->nkFont, app->fsctx, mainFont, UI_FONT_SIZE);

draw_init(&app->draw, app->fsctx);

Expand All @@ -216,8 +194,7 @@ void cleanup(void *user_data) {

ui_free(&app->circuit);

sg_destroy_sampler(app->font_smp);
sg_destroy_image(app->font_img);
nuklear_fontstash_free(&app->nkFont);

fsgp_destroy(app->fsctx);

Expand Down Expand Up @@ -261,8 +238,7 @@ void frame(void *user_data) {
}

struct nk_context *ctx = snk_new_frame();

nk_style_set_font(ctx, &app->latin->handle);
nk_style_set_font(ctx, &app->nkFont);

int width = sapp_width(), height = sapp_height();
sgp_begin(width, height);
Expand Down
6 changes: 3 additions & 3 deletions src/nonapple.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
*/

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_SOFTWARE_FONT
#define NK_IMPLEMENTATION
#define MSDF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
Expand All @@ -40,10 +39,11 @@
#include "sokol_glue.h"
#include "sokol_gp.h"
#include "sokol_log.h"
#include "sokol_nuklear.h"
#include "sokol_time.h"
#include "stb_image.h"
#include "strpool.h"

#include "render/sokol_nuklear.h"

#define STB_DS_IMPLEMENTATION
#include "stb_ds.h"
106 changes: 106 additions & 0 deletions src/render/fons_nuklear.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
Copyright 2024 Ryan "rj45" Sanche
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <stdlib.h>

#include "fons_nuklear.h"

static float nuklear_fontstash_width(
nk_handle handle, float height, const char *text, int len) {
nuklear_fonstash_font *nufons = handle.ptr;
FONScontext *fsctx = nufons->fsctx;
float bounds[4];
fonsPushState(fsctx);
fonsClearState(fsctx);
fonsSetSize(fsctx, height);
fonsSetFont(fsctx, nufons->font);
fonsSetAlign(fsctx, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);
fonsTextBounds(fsctx, 0, 0, text, text + len, bounds);
fonsPopState(fsctx);
return bounds[2] - bounds[0];
}

static void nuklear_fontstash_query_glyph(
nk_handle handle, float font_height, struct nk_user_font_glyph *glyph,
nk_rune codepoint, nk_rune next_codepoint) {
nuklear_fonstash_font *nufons = handle.ptr;
FONScontext *fsctx = nufons->fsctx;

FONStextIter iter;
FONSquad quad1, quad2;

fonsPushState(fsctx);
fonsClearState(fsctx);
fonsSetSize(fsctx, font_height);
fonsSetFont(fsctx, nufons->font);
fonsSetAlign(fsctx, FONS_ALIGN_LEFT | FONS_ALIGN_TOP);

// todo: convert codepoints to utf-8
char text[3] = {codepoint, next_codepoint, 0};

fonsTextIterInit(fsctx, &iter, 0, 0, text, text + (next_codepoint ? 2 : 1));
fonsTextIterNext(fsctx, &iter, &quad1);
if (next_codepoint) {
fonsTextIterNext(fsctx, &iter, &quad2);
}
fonsPopState(fsctx);

glyph->width = quad1.x1 - quad1.x0;
glyph->height = quad1.y1 - quad1.y0;
if (next_codepoint) {
glyph->xadvance = quad2.x0 - quad1.x0;
} else {
glyph->xadvance = quad1.x1 - quad1.x0;
}
glyph->uv[0].x = quad1.s0;
glyph->uv[0].y = quad1.t0;
glyph->uv[1].x = quad1.s1;
glyph->uv[1].y = quad1.t1;

// not sure about this
glyph->offset.x = quad1.x0;
glyph->offset.y = quad1.y0;
}

void fsgp_query_texture(FONScontext *ctx, sg_image *img, sg_sampler *smp);

void nuklear_fontstash_init(
struct nk_user_font *font, FONScontext *fsctx, int fontNum, float height) {
nuklear_fonstash_font *nukfonsfont = malloc(sizeof(nuklear_fonstash_font));
*nukfonsfont = (nuklear_fonstash_font){.fsctx = fsctx, .font = fontNum};

sg_image img;
sg_sampler smp;
fsgp_query_texture(fsctx, &img, &smp);

snk_image_t snk_img = snk_make_image(&(snk_image_desc_t){
.image = img,
.sampler = smp,
});

*font = (struct nk_user_font){
.userdata = nk_handle_ptr(nukfonsfont),
.height = height,
.width = nuklear_fontstash_width,
.query = nuklear_fontstash_query_glyph,
.texture = snk_nkhandle(snk_img),
};
}

void nuklear_fontstash_free(struct nk_user_font *font) {
free(font->userdata.ptr);
font->userdata.ptr = NULL;
}
44 changes: 44 additions & 0 deletions src/render/fons_nuklear.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Copyright 2024 Ryan "rj45" Sanche
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef FONS_NUKLEAR_H
#define FONS_NUKLEAR_H

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_SOFTWARE_FONT

#include "fontstash.h"
#include "nuklear.h"

#include "sokol_app.h"
#include "sokol_gfx.h"

#include "render/sokol_nuklear.h"

typedef struct nuklear_fonstash_font {
FONScontext *fsctx;
int font;
} nuklear_fonstash_font;

void nuklear_fontstash_init(
struct nk_user_font *font, FONScontext *fsctx, int fontNum, float height);

void nuklear_fontstash_free(struct nk_user_font *font);

#endif // FONS_NUKLEAR_H
Loading

0 comments on commit 674c8ae

Please sign in to comment.