text: Introduce new TrueType implementation
This commit is contained in:
parent
78c1c17307
commit
18f46976f8
16
apps/about.c
16
apps/about.c
@ -14,6 +14,7 @@
|
||||
#include <toaru/decorations.h>
|
||||
#include <toaru/sdf.h>
|
||||
#include <toaru/menu.h>
|
||||
#include <toaru/text.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
@ -26,6 +27,9 @@ static int32_t width = 350;
|
||||
static int32_t height = 250;
|
||||
static char * version_str;
|
||||
|
||||
static struct TT_Font * _tt_font_thin = NULL;
|
||||
static struct TT_Font * _tt_font_bold = NULL;
|
||||
|
||||
static char * icon_path;
|
||||
static char * title_str;
|
||||
static char * version_str;
|
||||
@ -40,7 +44,14 @@ static void draw_string(int y, const char * string, int font, uint32_t color) {
|
||||
struct decor_bounds bounds;
|
||||
decor_get_bounds(window, &bounds);
|
||||
|
||||
#if 0
|
||||
draw_sdf_string(ctx, bounds.left_width + center_x(draw_sdf_string_width(string, 16, font)), bounds.top_height + 10 + logo.height + 10 + y, string, 16, color, font);
|
||||
#else
|
||||
struct TT_Font * _tt_font = (font == SDF_FONT_BOLD ? _tt_font_bold : _tt_font_thin);
|
||||
tt_set_size(_tt_font, 13);
|
||||
tt_draw_string(ctx, _tt_font, bounds.left_width + center_x(tt_string_width(_tt_font, string)), bounds.top_height + 10 + logo.height + 10 + y + 13, string, color);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static void redraw(void) {
|
||||
@ -89,7 +100,7 @@ static void init_default(void) {
|
||||
sprintf(version_str, "ToaruOS %s", u.release);
|
||||
}
|
||||
|
||||
copyright_str[0] = "(C) 2011-2021 K. Lange, et al.";
|
||||
copyright_str[0] = "© 2011-2021 K. Lange, et al.";
|
||||
copyright_str[1] = "-";
|
||||
copyright_str[2] = "ToaruOS is free software released under the";
|
||||
copyright_str[3] = "NCSA/University of Illinois license.";
|
||||
@ -108,6 +119,9 @@ int main(int argc, char * argv[]) {
|
||||
}
|
||||
init_decorations();
|
||||
|
||||
_tt_font_thin = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf");
|
||||
_tt_font_bold = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf");
|
||||
|
||||
struct decor_bounds bounds;
|
||||
decor_get_bounds(NULL, &bounds);
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <toaru/list.h>
|
||||
#include <toaru/menu.h>
|
||||
#include <toaru/sdf.h>
|
||||
#include <toaru/text.h>
|
||||
|
||||
/* 16- and 256-color palette */
|
||||
#include "terminal-palette.h"
|
||||
@ -112,6 +113,11 @@ static bool _have_freetype = 0; /* Whether freetype is available */
|
||||
static bool _force_no_ft = 0; /* Whether to force disable the freetype backend */
|
||||
static bool _free_size = 1; /* Disable rounding when resized */
|
||||
|
||||
static struct TT_Font * _tt_font_normal = NULL;
|
||||
static struct TT_Font * _tt_font_bold = NULL;
|
||||
static struct TT_Font * _tt_font_oblique = NULL;
|
||||
static struct TT_Font * _tt_font_bold_oblique = NULL;
|
||||
|
||||
/** Freetype extension renderer functions */
|
||||
static void (*freetype_set_font_face)(int face) = NULL;
|
||||
static void (*freetype_set_font_size)(int size) = NULL;
|
||||
@ -684,29 +690,39 @@ static void term_write_char(uint32_t val, uint16_t x, uint16_t y, uint32_t fg, u
|
||||
|
||||
/* Draw glyphs */
|
||||
if (_use_aa && !_have_freetype) {
|
||||
/* Draw using the Toaru SDF rendering library */
|
||||
char tmp[7];
|
||||
to_eight(val, tmp);
|
||||
if (val == 0xFFFF) return;
|
||||
for (uint8_t i = 0; i < char_height; ++i) {
|
||||
for (uint8_t j = 0; j < char_width; ++j) {
|
||||
term_set_point(x+j,y+i,_bg);
|
||||
}
|
||||
}
|
||||
if (val != 0 && val != ' ' && _fg != _bg) {
|
||||
int _font = SDF_FONT_MONO;
|
||||
if (flags & ANSI_BOLD && flags & ANSI_ITALIC) {
|
||||
_font = SDF_FONT_MONO_BOLD_OBLIQUE;
|
||||
} else if (flags & ANSI_BOLD) {
|
||||
_font = SDF_FONT_MONO_BOLD;
|
||||
} else if (flags & ANSI_ITALIC) {
|
||||
_font = SDF_FONT_MONO_OBLIQUE;
|
||||
}
|
||||
if (_no_frame) {
|
||||
draw_sdf_string_gamma(ctx, x-1, y, tmp, font_size, _fg, _font, font_gamma);
|
||||
} else {
|
||||
draw_sdf_string_gamma(ctx, x+decor_left_width-1, y+decor_top_height+menu_bar_height, tmp, font_size, _fg, _font, font_gamma);
|
||||
if (flags & ANSI_WIDE) {
|
||||
for (uint8_t i = 0; i < char_height; ++i) {
|
||||
for (uint8_t j = char_width; j < 2 * char_width; ++j) {
|
||||
term_set_point(x+j,y+i,_bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val < 32 || val == ' ') {
|
||||
goto _extra_stuff;
|
||||
}
|
||||
struct TT_Font * _font = _tt_font_normal;
|
||||
if (flags & ANSI_BOLD && flags & ANSI_ITALIC) {
|
||||
_font = _tt_font_bold_oblique;
|
||||
} else if (flags & ANSI_BOLD) {
|
||||
_font = _tt_font_bold;
|
||||
} else if (flags & ANSI_ITALIC) {
|
||||
_font = _tt_font_oblique;
|
||||
}
|
||||
unsigned int glyph = tt_glyph_for_codepoint(_font, val);
|
||||
tt_set_size(_font, char_offset);
|
||||
int _x = x;
|
||||
int _y = y + char_offset;
|
||||
if (!_no_frame) {
|
||||
_x += decor_left_width ;
|
||||
_y += decor_top_height + menu_bar_height;
|
||||
}
|
||||
tt_draw_glyph(ctx, _font, _x, _y, glyph, _fg);
|
||||
} else if (_use_aa && _have_freetype) {
|
||||
/* Draw using freetype extension */
|
||||
if (val == 0xFFFF) { return; } /* Unicode, do not redraw here */
|
||||
@ -1779,10 +1795,12 @@ static void reinit(void) {
|
||||
char_width = 9;
|
||||
char_height = 17;
|
||||
font_size = 16;
|
||||
char_offset = 13;
|
||||
if (scale_fonts) {
|
||||
font_size *= font_scaling;
|
||||
char_height *= font_scaling;
|
||||
char_width *= font_scaling;
|
||||
char_offset *= font_scaling;
|
||||
}
|
||||
} else if (_use_aa && _have_freetype) {
|
||||
font_size = 13;
|
||||
@ -2299,6 +2317,11 @@ int main(int argc, char ** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
_tt_font_normal = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf");
|
||||
_tt_font_bold = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf");
|
||||
_tt_font_oblique = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Oblique.ttf");
|
||||
_tt_font_bold_oblique = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSansMono-BoldOblique.ttf");
|
||||
|
||||
/* Initialize the windowing library */
|
||||
yctx = yutani_init();
|
||||
|
||||
|
BIN
base/test.png
BIN
base/test.png
Binary file not shown.
Before Width: | Height: | Size: 5.4 KiB |
11
base/usr/include/toaru/text.h
Normal file
11
base/usr/include/toaru/text.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern struct TT_Font * tt_font_from_file(const char * fileName);
|
||||
extern int tt_glyph_for_codepoint(struct TT_Font * font, unsigned int codepoint);
|
||||
extern void tt_draw_glyph(gfx_context_t * ctx, struct TT_Font * font, int x_offset, int y_offset, unsigned int glyph, uint32_t color);
|
||||
extern void tt_set_size(struct TT_Font * font, int size);
|
||||
extern int tt_xadvance_for_glyph(struct TT_Font * font, unsigned int ind);
|
||||
extern int tt_string_width(struct TT_Font * font, const char * s);
|
||||
extern int tt_draw_string(gfx_context_t * ctx, struct TT_Font * font, int x, int y, const char * s, uint32_t color);
|
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans-BoldOblique.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans-BoldOblique.ttf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans-Oblique.ttf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf
Normal file
Binary file not shown.
Binary file not shown.
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Oblique.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Oblique.ttf
Normal file
Binary file not shown.
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf
Normal file
BIN
base/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf
Normal file
Binary file not shown.
@ -15,6 +15,7 @@
|
||||
#include <toaru/graphics.h>
|
||||
#include <toaru/decorations.h>
|
||||
#include <toaru/sdf.h>
|
||||
#include <toaru/text.h>
|
||||
|
||||
#define INACTIVE 10
|
||||
|
||||
@ -27,6 +28,7 @@ static int mr_width = 6;
|
||||
static int l_height = 9;
|
||||
static int ll_width = 9;
|
||||
static int lr_width = 9;
|
||||
static struct TT_Font * _tt_font = NULL;
|
||||
|
||||
static sprite_t * sprites[20];
|
||||
|
||||
@ -160,6 +162,19 @@ static void render_decorations_fancy(yutani_window_t * window, gfx_context_t * c
|
||||
freetype_draw_string(ctx, title_offset, TEXT_OFFSET + 14, title_color, tmp_title);
|
||||
}
|
||||
} else {
|
||||
tt_set_size(_tt_font, 12);
|
||||
if (tt_string_width(_tt_font, tmp_title) + EXTRA_SPACE > width) {
|
||||
while (t_l >= 0 && (tt_string_width(_tt_font, tmp_title) + EXTRA_SPACE > width)) {
|
||||
tmp_title[t_l] = '\0';
|
||||
t_l--;
|
||||
}
|
||||
}
|
||||
|
||||
if (*tmp_title) {
|
||||
int title_offset = (width / 2) - (tt_string_width(_tt_font, tmp_title) / 2);
|
||||
tt_draw_string(ctx, _tt_font, title_offset, TEXT_OFFSET + 14, tmp_title, title_color);
|
||||
}
|
||||
#if 0
|
||||
#define TEXT_SIZE 15
|
||||
if (draw_sdf_string_width(tmp_title, TEXT_SIZE, SDF_FONT_BOLD) + EXTRA_SPACE > width) {
|
||||
while (t_l >= 0 && (draw_sdf_string_width(tmp_title, TEXT_SIZE, SDF_FONT_BOLD) + EXTRA_SPACE > width)) {
|
||||
@ -172,6 +187,7 @@ static void render_decorations_fancy(yutani_window_t * window, gfx_context_t * c
|
||||
int title_offset = (width / 2) - (draw_sdf_string_width(tmp_title, TEXT_SIZE, SDF_FONT_BOLD) / 2);
|
||||
draw_sdf_string(ctx, title_offset, TEXT_OFFSET+2, tmp_title, TEXT_SIZE, title_color, SDF_FONT_BOLD);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
free(tmp_title);
|
||||
@ -226,6 +242,8 @@ void decor_init() {
|
||||
decor_check_button_press = check_button_press_fancy;
|
||||
decor_get_bounds = get_bounds_fancy;
|
||||
|
||||
_tt_font = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf");
|
||||
|
||||
void * freetype = dlopen("libtoaru_ext_freetype_fonts.so", 0);
|
||||
if (freetype) {
|
||||
_have_freetype = 1;
|
||||
|
12
lib/menu.c
12
lib/menu.c
@ -21,6 +21,7 @@
|
||||
#include <toaru/hashmap.h>
|
||||
#include <toaru/list.h>
|
||||
#include <toaru/icon_cache.h>
|
||||
#include <toaru/text.h>
|
||||
|
||||
#include <toaru/menu.h>
|
||||
|
||||
@ -36,6 +37,8 @@
|
||||
static hashmap_t * menu_windows = NULL;
|
||||
static yutani_t * my_yctx = NULL;
|
||||
|
||||
static struct TT_Font * _tt_font = NULL;
|
||||
|
||||
static struct MenuList * hovered_menu = NULL;
|
||||
|
||||
int menu_definitely_close(struct MenuList * menu);
|
||||
@ -50,6 +53,7 @@ static int (*freetype_draw_string_width)(char * s) = NULL;
|
||||
__attribute__((constructor))
|
||||
static void _init_menus(void) {
|
||||
menu_windows = hashmap_create_int(10);
|
||||
_tt_font = tt_font_from_file("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf");
|
||||
void * freetype = dlopen("libtoaru_ext_freetype_fonts.so", 0);
|
||||
if (freetype) {
|
||||
_have_freetype = 1;
|
||||
@ -70,7 +74,9 @@ static int string_width(const char * s) {
|
||||
freetype_set_font_size(13);
|
||||
return freetype_draw_string_width((char *)s);
|
||||
} else {
|
||||
return draw_sdf_string_width((char *)s, 16, SDF_FONT_THIN);
|
||||
tt_set_size(_tt_font, 13);
|
||||
return tt_string_width(_tt_font, s);
|
||||
//return draw_sdf_string_width((char *)s, 16, SDF_FONT_THIN);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +86,9 @@ static int draw_string(gfx_context_t * ctx, int x, int y, uint32_t color, const
|
||||
freetype_set_font_size(13);
|
||||
return freetype_draw_string(ctx, x+2, y + 13 /* I think? */, color, s);
|
||||
} else {
|
||||
return draw_sdf_string(ctx, x, y, s, 16, color, SDF_FONT_THIN);
|
||||
tt_set_size(_tt_font, 13);
|
||||
return tt_draw_string(ctx, _tt_font, x, y + 13, s, color);
|
||||
//return draw_sdf_string(ctx, x, y, s, 16, color, SDF_FONT_THIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
754
lib/text.c
Normal file
754
lib/text.c
Normal file
@ -0,0 +1,754 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <toaru/graphics.h>
|
||||
#include <toaru/decodeutf8.h>
|
||||
|
||||
#undef min
|
||||
#define min(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
struct TT_Table {
|
||||
off_t offset;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
struct TT_Coord {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
struct TT_Edge {
|
||||
struct TT_Coord start;
|
||||
struct TT_Coord end;
|
||||
int direction;
|
||||
};
|
||||
|
||||
struct TT_Contour {
|
||||
size_t edgeCount;
|
||||
size_t nextAlloc;
|
||||
size_t flags;
|
||||
size_t last_start;
|
||||
struct TT_Edge edges[];
|
||||
};
|
||||
|
||||
struct TT_Intersection {
|
||||
float x;
|
||||
int affect;
|
||||
};
|
||||
|
||||
struct TT_Shape {
|
||||
size_t edgeCount;
|
||||
int lastY;
|
||||
int startY;
|
||||
struct TT_Edge edges[];
|
||||
};
|
||||
|
||||
struct TT_Vertex {
|
||||
unsigned char flags;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
#if 0
|
||||
static int edge_sorter_high_scanline(const void * a, const void * b) {
|
||||
const struct TT_Edge * left = a;
|
||||
const struct TT_Edge * right = b;
|
||||
|
||||
if (left->start.y < right->start.y) return -1;
|
||||
if (left->start.y > right->start.y) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sort_edges(size_t edgeCount, struct TT_Edge edges[edgeCount]) {
|
||||
qsort(edges, edgeCount, sizeof(struct TT_Edge), edge_sorter_high_scanline);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int intersection_sorter(const void * a, const void * b) {
|
||||
const struct TT_Intersection * left = a;
|
||||
const struct TT_Intersection * right = b;
|
||||
|
||||
if (left->x < right->x) return -1;
|
||||
if (left->x > right->x) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sort_intersections(size_t cnt, struct TT_Intersection intersections[cnt]) {
|
||||
qsort(intersections, cnt, sizeof(struct TT_Intersection), intersection_sorter);
|
||||
}
|
||||
|
||||
static size_t prune_edges(size_t edgeCount, float y, struct TT_Edge edges[edgeCount], struct TT_Edge into[edgeCount]) {
|
||||
size_t outWriter = 0;
|
||||
for (size_t i = 0; i < edgeCount; ++i) {
|
||||
if (y > edges[i].start.y && y > edges[i].end.y) continue;
|
||||
if (y <= edges[i].start.y && y <= edges[i].end.y) continue; //break;
|
||||
into[outWriter++] = edges[i];
|
||||
}
|
||||
return outWriter;
|
||||
}
|
||||
|
||||
static float edge_at(float y, struct TT_Edge * edge) {
|
||||
float u = (y - edge->start.y) / (edge->end.y - edge->start.y);
|
||||
return edge->start.x + u * (edge->end.x - edge->start.x);
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXME: This spans the whole context; it should use the path's bounding box...
|
||||
*/
|
||||
void tt_path_paint(gfx_context_t * ctx, struct TT_Shape * shape, uint32_t color) {
|
||||
size_t size = shape->edgeCount;
|
||||
struct TT_Edge * intersects = malloc(sizeof(struct TT_Edge) * size);
|
||||
struct TT_Intersection * crosses = malloc(sizeof(struct TT_Intersection) * size);
|
||||
float * subsamples = malloc(sizeof(float) * ctx->width);
|
||||
memset(subsamples, 0, sizeof(float) * ctx->width);
|
||||
|
||||
/* We have sorted by the scanline at which the line becomes active, so we should be able to do this... */
|
||||
int yres = 4;
|
||||
for (int y = shape->startY; y < shape->lastY; ++y) {
|
||||
/* Figure out which ones fit here */
|
||||
float _y = y;
|
||||
int start_x = ctx->width;
|
||||
int max_x = 0;
|
||||
for (int l = 0; l < yres; ++l) {
|
||||
size_t cnt = prune_edges(size, _y, shape->edges, intersects);
|
||||
if (cnt) {
|
||||
/* Get intersections */
|
||||
for (size_t j = 0; j < cnt; ++j) {
|
||||
crosses[j].x = edge_at(_y,&intersects[j]);
|
||||
crosses[j].affect = intersects[j].direction;
|
||||
}
|
||||
|
||||
/* Now sort the intersections */
|
||||
sort_intersections(cnt, crosses);
|
||||
|
||||
if (crosses[0].x < start_x) start_x = crosses[0].x;
|
||||
if (crosses[cnt-1].x+1 > max_x) max_x = crosses[cnt-1].x+1;
|
||||
|
||||
int wind = 0;
|
||||
size_t j = 0;
|
||||
for (int x = 0; x < ctx->width && j < cnt; ++x) {
|
||||
while (j < cnt && x > crosses[j].x) {
|
||||
wind += crosses[j].affect;
|
||||
j++;
|
||||
}
|
||||
float last = x;
|
||||
while (j < cnt && (x+1) > crosses[j].x) {
|
||||
if (wind != 0) {
|
||||
subsamples[x] += crosses[j].x - last;
|
||||
}
|
||||
last = crosses[j].x;
|
||||
wind += crosses[j].affect;
|
||||
j++;
|
||||
}
|
||||
if (wind != 0) {
|
||||
subsamples[x] += (x+1) - last;
|
||||
}
|
||||
}
|
||||
}
|
||||
_y += 1.0/(float)yres;
|
||||
}
|
||||
if (start_x < 0) start_x = 0;
|
||||
for (int x = start_x; x < max_x && x < ctx->width; ++x) {
|
||||
if (x < 0 || y < 0 || x >= ctx->width || y >= ctx->height) return;
|
||||
#ifdef __toaru__
|
||||
unsigned int c = subsamples[x] / (float)yres * (float)_ALP(color);
|
||||
uint32_t nc = premultiply((color & 0xFFFFFF) | ((c & 0xFF) << 24));
|
||||
GFX(ctx, x, y) = alpha_blend_rgba(GFX(ctx, x, y), nc);
|
||||
#else
|
||||
unsigned int c = subsamples[x] / (float)yres * 255;
|
||||
draw_pixel(x,y,alpha_blend(get_pixel(x,y), color, c));
|
||||
#endif
|
||||
subsamples[x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
free(subsamples);
|
||||
free(crosses);
|
||||
free(intersects);
|
||||
}
|
||||
|
||||
struct TT_Contour * tt_contour_line_to(struct TT_Contour * shape, float x, float y) {
|
||||
if (shape->flags & 1) {
|
||||
shape->edges[shape->edgeCount].end.x = x;
|
||||
shape->edges[shape->edgeCount].end.y = y;
|
||||
shape->edgeCount++;
|
||||
shape->flags &= ~1;
|
||||
} else {
|
||||
if (shape->edgeCount + 1 == shape->nextAlloc) {
|
||||
shape->nextAlloc *= 2;
|
||||
shape = realloc(shape, sizeof(struct TT_Contour) + sizeof(struct TT_Edge) * (shape->nextAlloc));
|
||||
}
|
||||
shape->edges[shape->edgeCount].start.x = shape->edges[shape->edgeCount-1].end.x;
|
||||
shape->edges[shape->edgeCount].start.y = shape->edges[shape->edgeCount-1].end.y;
|
||||
shape->edges[shape->edgeCount].end.x = x;
|
||||
shape->edges[shape->edgeCount].end.y = y;
|
||||
shape->edgeCount++;
|
||||
shape->flags &= ~1;
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
struct TT_Contour * tt_contour_move_to(struct TT_Contour * shape, float x, float y) {
|
||||
if (!(shape->flags & 1) && shape->edgeCount) {
|
||||
shape = tt_contour_line_to(shape, shape->edges[shape->last_start].start.x, shape->edges[shape->last_start].start.y);
|
||||
}
|
||||
if (shape->edgeCount + 1 == shape->nextAlloc) {
|
||||
shape->nextAlloc *= 2;
|
||||
shape = realloc(shape, sizeof(struct TT_Contour) + sizeof(struct TT_Edge) * (shape->nextAlloc));
|
||||
}
|
||||
shape->edges[shape->edgeCount].start.x = x;
|
||||
shape->edges[shape->edgeCount].start.y = y;
|
||||
shape->last_start = shape->edgeCount;
|
||||
shape->flags |= 1;
|
||||
return shape;
|
||||
}
|
||||
|
||||
struct TT_Contour * tt_contour_start(float x, float y) {
|
||||
struct TT_Contour * shape = malloc(sizeof(struct TT_Contour) + sizeof(struct TT_Edge) * 2);
|
||||
shape->edgeCount = 0;
|
||||
shape->nextAlloc = 2;
|
||||
shape->flags = 0;
|
||||
shape->last_start = 0;
|
||||
shape->edges[shape->edgeCount].start.x = x;
|
||||
shape->edges[shape->edgeCount].start.y = y;
|
||||
shape->last_start = shape->edgeCount;
|
||||
shape->flags |= 1;
|
||||
return shape;
|
||||
}
|
||||
|
||||
struct TT_Shape * tt_contour_finish(struct TT_Contour * in) {
|
||||
size_t size = in->edgeCount + 1;
|
||||
struct TT_Shape * tmp = malloc(sizeof(struct TT_Shape) + sizeof(struct TT_Edge) * size);
|
||||
memcpy(tmp->edges, in->edges, sizeof(struct TT_Edge) * in->edgeCount);
|
||||
|
||||
if (in->flags & 1) {
|
||||
size--;
|
||||
} else {
|
||||
tmp->edges[in->edgeCount].start.x = in->edges[in->edgeCount-1].end.x;
|
||||
tmp->edges[in->edgeCount].start.y = in->edges[in->edgeCount-1].end.y;
|
||||
tmp->edges[in->edgeCount].end.x = in->edges[in->last_start].start.x;
|
||||
tmp->edges[in->edgeCount].end.y = in->edges[in->last_start].start.y;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (tmp->edges[i].start.y < tmp->edges[i].end.y) {
|
||||
tmp->edges[i].direction = 1;
|
||||
} else {
|
||||
tmp->edges[i].direction = -1;
|
||||
struct TT_Coord j = tmp->edges[i].start;
|
||||
tmp->edges[i].start = tmp->edges[i].end;
|
||||
tmp->edges[i].end = j;
|
||||
}
|
||||
}
|
||||
|
||||
//sort_edges(size, tmp->edges);
|
||||
tmp->edgeCount = size;
|
||||
tmp->startY = 1000000;
|
||||
tmp->lastY = 0;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
if (tmp->edges[i].end.y + 1 > tmp->lastY) tmp->lastY = tmp->edges[i].end.y + 1;
|
||||
if (tmp->edges[i].start.y < tmp->startY) tmp->startY = tmp->edges[i].start.y;
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opaque data struct.
|
||||
*/
|
||||
struct TT_Font {
|
||||
int privFlags;
|
||||
FILE * filePtr;
|
||||
uint8_t * buffer;
|
||||
uint8_t * memPtr;
|
||||
|
||||
struct TT_Table head_ptr;
|
||||
struct TT_Table cmap_ptr;
|
||||
struct TT_Table loca_ptr;
|
||||
struct TT_Table glyf_ptr;
|
||||
struct TT_Table hhea_ptr;
|
||||
struct TT_Table hmtx_ptr;
|
||||
|
||||
off_t cmap_start;
|
||||
|
||||
size_t cmap_maxInd;
|
||||
|
||||
float scale;
|
||||
};
|
||||
|
||||
static inline int tt_seek(struct TT_Font * font, off_t offset) {
|
||||
if (font->privFlags & 1) {
|
||||
return fseek(font->filePtr, offset, SEEK_SET);
|
||||
} else {
|
||||
font->memPtr = font->buffer + offset;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline long tt_tell(struct TT_Font * font) {
|
||||
if (font->privFlags & 1) {
|
||||
return ftell(font->filePtr);
|
||||
} else {
|
||||
return font->memPtr - font->buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t tt_read_8(struct TT_Font * font) {
|
||||
if (font->privFlags & 1) {
|
||||
return fgetc(font->filePtr);
|
||||
} else {
|
||||
return *(font->memPtr++);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t tt_read_32(struct TT_Font * font) {
|
||||
int a = tt_read_8(font);
|
||||
int b = tt_read_8(font);
|
||||
int c = tt_read_8(font);
|
||||
int d = tt_read_8(font);
|
||||
if (a < 0 || b < 0 || c < 0 || d < 0) return 0;
|
||||
return ((a & 0xFF) << 24) |
|
||||
((b & 0xFF) << 16) |
|
||||
((c & 0xFF) << 8) |
|
||||
((d & 0xFF) << 0);
|
||||
}
|
||||
|
||||
static inline uint16_t tt_read_16(struct TT_Font * font) {
|
||||
int a = tt_read_8(font);
|
||||
int b = tt_read_8(font);
|
||||
if (a < 0 || b < 0) return 0;
|
||||
return ((a & 0xFF) << 8) |
|
||||
((b & 0xFF) << 0);
|
||||
}
|
||||
|
||||
int tt_xadvance_for_glyph(struct TT_Font * font, unsigned int ind) {
|
||||
tt_seek(font, font->hhea_ptr.offset + 2 * 17);
|
||||
uint16_t numLong = tt_read_16(font);
|
||||
|
||||
if (ind < numLong) {
|
||||
tt_seek(font, font->hmtx_ptr.offset + ind * 4);
|
||||
return tt_read_16(font);
|
||||
}
|
||||
|
||||
tt_seek(font, font->hmtx_ptr.offset + (numLong - 1) * 4);
|
||||
return tt_read_16(font);
|
||||
}
|
||||
|
||||
void tt_set_size(struct TT_Font * font, int size) {
|
||||
font->scale = (float)size / 2200.0; /* shoot */
|
||||
}
|
||||
|
||||
off_t tt_get_glyph_offset(struct TT_Font * font, unsigned int glyph) {
|
||||
tt_seek(font, font->loca_ptr.offset + glyph * 4);
|
||||
return tt_read_32(font);
|
||||
}
|
||||
|
||||
int tt_glyph_for_codepoint(struct TT_Font * font, unsigned int codepoint) {
|
||||
/* Get group count */
|
||||
tt_seek(font, font->cmap_start + 8);
|
||||
uint32_t ngroups = tt_read_32(font);
|
||||
|
||||
for (unsigned int i = 0; i < ngroups; ++i) {
|
||||
uint32_t start = tt_read_32(font);
|
||||
uint32_t end = tt_read_32(font);
|
||||
uint32_t ind = tt_read_32(font);
|
||||
|
||||
if (codepoint >= start && codepoint <= end) {
|
||||
return ind + (codepoint - start);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void midpoint(float x_0, float y_0, float cx, float cy, float x_1, float y_1, float t, float * outx, float * outy) {
|
||||
float t2 = t * t;
|
||||
float nt = 1.0 - t;
|
||||
float nt2 = nt * nt;
|
||||
*outx = nt2 * x_0 + 2 * t * nt * cx + t2 * x_1;
|
||||
*outy = nt2 * y_0 + 2 * t * nt * cy + t2 * y_1;
|
||||
}
|
||||
|
||||
static struct TT_Contour * tt_draw_glyph_into(struct TT_Contour * contour, struct TT_Font * font, float x_offset, float y_offset, unsigned int glyph) {
|
||||
off_t glyf_offset = tt_get_glyph_offset(font, glyph);
|
||||
if (tt_get_glyph_offset(font, glyph + 1) == glyf_offset) return contour;
|
||||
|
||||
tt_seek(font, font->glyf_ptr.offset + glyf_offset);
|
||||
|
||||
int16_t numContours = tt_read_16(font);
|
||||
/* int16_t xMin = */ tt_read_16(font);
|
||||
/* int16_t yMin = */ tt_read_16(font);
|
||||
/* int16_t xMax = */ tt_read_16(font);
|
||||
/* int16_t yMax = */ tt_read_16(font);
|
||||
|
||||
tt_seek(font, font->glyf_ptr.offset + glyf_offset + 10);
|
||||
|
||||
if (numContours > 0) {
|
||||
uint16_t endPt;
|
||||
for (int i = 0; i < numContours; ++i) {
|
||||
endPt = tt_read_16(font);
|
||||
}
|
||||
uint16_t numInstr = tt_read_16(font);
|
||||
for (unsigned int i = 0; i < numInstr; ++i) {
|
||||
tt_read_8(font);
|
||||
}
|
||||
struct TT_Vertex * vertices = malloc(sizeof(struct TT_Vertex) * (endPt + 1));
|
||||
for (int i = 0; i < endPt + 1; ) {
|
||||
uint8_t v = tt_read_8(font);
|
||||
vertices[i].flags = v;
|
||||
i++;
|
||||
if (v & 8) {
|
||||
uint8_t repC = tt_read_8(font);
|
||||
while (repC) {
|
||||
vertices[i].flags = v;
|
||||
repC--;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
int last_x = 0;
|
||||
int last_y = 0;
|
||||
for (int i = 0; i < endPt + 1; i++) {
|
||||
unsigned char flags = vertices[i].flags;
|
||||
if (flags & (1 << 1)) {
|
||||
/* One byte */
|
||||
if (flags & (1 << 4)) {
|
||||
/* Positive */
|
||||
vertices[i].x = last_x + tt_read_8(font);
|
||||
} else {
|
||||
vertices[i].x = last_x - tt_read_8(font);
|
||||
}
|
||||
} else {
|
||||
if (flags & (1 << 4)) {
|
||||
vertices[i].x = last_x;
|
||||
} else {
|
||||
int16_t diff = tt_read_16(font);
|
||||
vertices[i].x = last_x + diff;
|
||||
}
|
||||
}
|
||||
last_x = vertices[i].x;
|
||||
}
|
||||
for (int i = 0; i < endPt + 1; i++) {
|
||||
unsigned char flags = vertices[i].flags;
|
||||
if (flags & (1 << 2)) {
|
||||
/* One byte */
|
||||
if (flags & (1 << 5)) {
|
||||
/* Positive */
|
||||
vertices[i].y = last_y + tt_read_8(font);
|
||||
} else {
|
||||
vertices[i].y = last_y - tt_read_8(font);
|
||||
}
|
||||
} else {
|
||||
if (flags & (1 << 5)) {
|
||||
vertices[i].y = last_y;
|
||||
} else {
|
||||
int16_t diff = tt_read_16(font);
|
||||
vertices[i].y = last_y + diff;
|
||||
}
|
||||
}
|
||||
last_y = vertices[i].y;
|
||||
}
|
||||
|
||||
tt_seek(font, font->glyf_ptr.offset + glyf_offset + 10);
|
||||
|
||||
int move_next = 1;
|
||||
int next_end = tt_read_16(font);
|
||||
|
||||
float lx = 0, ly = 0, cx = 0, cy = 0, x = 0, y = 0;
|
||||
float sx = 0, sy = 0;
|
||||
int wasControl = 0;
|
||||
|
||||
for (int i = 0; i < endPt + 1; ++i) {
|
||||
x = ((float)vertices[i].x) * font->scale + x_offset;
|
||||
y = (-(float)vertices[i].y) * font->scale + y_offset;
|
||||
int isCurve = !(vertices[i].flags & (1 << 0));
|
||||
if (move_next) {
|
||||
contour = tt_contour_move_to(contour, x, y);
|
||||
if (isCurve) {
|
||||
fprintf(stderr, "*** Warning: Contour starts off curve\n");
|
||||
}
|
||||
move_next = 0;
|
||||
lx = x;
|
||||
ly = y;
|
||||
sx = x;
|
||||
sy = y;
|
||||
wasControl = 0;
|
||||
} else {
|
||||
if (isCurve) {
|
||||
if (wasControl) {
|
||||
float dx = (cx + x) / 2.0;
|
||||
float dy = (cy + y) / 2.0;
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
float mx, my;
|
||||
midpoint(lx,ly,cx,cy,dx,dy,(float)i / 10.0,&mx,&my);
|
||||
contour = tt_contour_line_to(contour, mx, my);
|
||||
}
|
||||
contour = tt_contour_line_to(contour, dx, dy);
|
||||
lx = dx;
|
||||
ly = dy;
|
||||
}
|
||||
cx = x;
|
||||
cy = y;
|
||||
wasControl = 1;
|
||||
} else {
|
||||
if (wasControl) {
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
float mx, my;
|
||||
midpoint(lx,ly,cx,cy,x,y,(float)i / 10.0,&mx,&my);
|
||||
contour = tt_contour_line_to(contour, mx, my);
|
||||
}
|
||||
}
|
||||
contour = tt_contour_line_to(contour, x, y);
|
||||
lx = x;
|
||||
ly = y;
|
||||
wasControl = 0;
|
||||
}
|
||||
}
|
||||
if (i == next_end) {
|
||||
if (wasControl) {
|
||||
for (int i = 1; i < 10; ++i) {
|
||||
float mx, my;
|
||||
midpoint(lx,ly,cx,cy,sx,sy,(float)i / 10.0,&mx,&my);
|
||||
contour = tt_contour_line_to(contour, mx, my);
|
||||
}
|
||||
}
|
||||
contour = tt_contour_line_to(contour, sx, sy);
|
||||
move_next = 1;
|
||||
next_end = tt_read_16(font);
|
||||
}
|
||||
}
|
||||
|
||||
free(vertices);
|
||||
} else if (numContours < 0) {
|
||||
while (1) {
|
||||
uint16_t flags = tt_read_16(font);
|
||||
uint16_t ind = tt_read_16(font);
|
||||
int16_t x, y;
|
||||
if (flags & (1 << 0)) {
|
||||
x = tt_read_16(font);
|
||||
y = tt_read_16(font);
|
||||
} else {
|
||||
x = tt_read_8(font);
|
||||
y = tt_read_8(font);
|
||||
}
|
||||
|
||||
float x_f = x_offset;
|
||||
float y_f = y_offset;
|
||||
if (flags & (1 << 1)) {
|
||||
x_f = x_offset + x * font->scale;
|
||||
y_f = y_offset - y * font->scale;
|
||||
}
|
||||
|
||||
if (flags & (1 << 3)) {
|
||||
/* TODO */
|
||||
tt_read_16(font);
|
||||
} else if (flags & (1 << 6)) {
|
||||
/* TODO */
|
||||
tt_read_16(font);
|
||||
tt_read_16(font);
|
||||
} else if (flags & (1 << 7)) {
|
||||
/* TODO */
|
||||
tt_read_16(font);
|
||||
tt_read_16(font);
|
||||
tt_read_16(font);
|
||||
tt_read_16(font);
|
||||
} else {
|
||||
long o = tt_tell(font);
|
||||
contour = tt_draw_glyph_into(contour,font,x_f,y_f,ind);
|
||||
tt_seek(font, o);
|
||||
}
|
||||
if (!(flags & (1 << 5))) break;
|
||||
}
|
||||
}
|
||||
|
||||
return contour;
|
||||
}
|
||||
|
||||
void tt_draw_glyph(gfx_context_t * ctx, struct TT_Font * font, int x, int y, unsigned int glyph, uint32_t color) {
|
||||
struct TT_Contour * contour = tt_contour_start(0, 0);
|
||||
contour = tt_draw_glyph_into(contour,font,x,y,glyph);
|
||||
if (contour->edgeCount) {
|
||||
struct TT_Shape * shape = tt_contour_finish(contour);
|
||||
tt_path_paint(ctx, shape, color);
|
||||
free(shape);
|
||||
}
|
||||
free(contour);
|
||||
}
|
||||
|
||||
int tt_string_width(struct TT_Font * font, const char * s) {
|
||||
float x_offset = 0;
|
||||
uint32_t cp = 0;
|
||||
uint32_t istate = 0;
|
||||
|
||||
for (const unsigned char * c = (const unsigned char*)s; *c; ++c) {
|
||||
if (!decode(&istate, &cp, *c)) {
|
||||
unsigned int glyph = tt_glyph_for_codepoint(font, cp);
|
||||
x_offset += tt_xadvance_for_glyph(font, glyph) * font->scale;
|
||||
}
|
||||
}
|
||||
|
||||
return x_offset;
|
||||
}
|
||||
|
||||
int tt_draw_string(gfx_context_t * ctx, struct TT_Font * font, int x, int y, const char * s, uint32_t color) {
|
||||
struct TT_Contour * contour = tt_contour_start(0, 0);
|
||||
|
||||
float x_offset = x;
|
||||
uint32_t cp = 0;
|
||||
uint32_t istate = 0;
|
||||
|
||||
for (const unsigned char * c = (const unsigned char*)s; *c; ++c) {
|
||||
if (!decode(&istate, &cp, *c)) {
|
||||
unsigned int glyph = tt_glyph_for_codepoint(font, cp);
|
||||
contour = tt_draw_glyph_into(contour,font,x_offset,y,glyph);
|
||||
x_offset += tt_xadvance_for_glyph(font, glyph) * font->scale;
|
||||
}
|
||||
}
|
||||
|
||||
if (contour->edgeCount) {
|
||||
struct TT_Shape * shape = tt_contour_finish(contour);
|
||||
tt_path_paint(ctx, shape, color);
|
||||
free(shape);
|
||||
}
|
||||
free(contour);
|
||||
|
||||
return x_offset - x;
|
||||
}
|
||||
|
||||
|
||||
static int tt_font_load(struct TT_Font * font) {
|
||||
if (tt_seek(font, 4)) goto _fail_free;
|
||||
uint16_t numTables = tt_read_16(font);
|
||||
if (tt_seek(font, 12)) goto _fail_free;
|
||||
|
||||
for (unsigned int i = 0; i < numTables; ++i) {
|
||||
uint32_t tag = tt_read_32(font);
|
||||
/* uint32_t checkSum = */ tt_read_32(font);
|
||||
uint32_t offset = tt_read_32(font);
|
||||
uint32_t length = tt_read_32(font);
|
||||
|
||||
switch (tag) {
|
||||
case 0x68656164: /* head */
|
||||
font->head_ptr.offset = offset;
|
||||
font->head_ptr.length = length;
|
||||
break;
|
||||
case 0x636d6170: /* cmap */
|
||||
font->cmap_ptr.offset = offset;
|
||||
font->cmap_ptr.length = length;
|
||||
break;
|
||||
case 0x676c7966: /* glyf */
|
||||
font->glyf_ptr.offset = offset;
|
||||
font->glyf_ptr.length = length;
|
||||
break;
|
||||
case 0x6c6f6361: /* loca */
|
||||
font->loca_ptr.offset = offset;
|
||||
font->loca_ptr.length = length;
|
||||
break;
|
||||
case 0x68686561: /* hhea */
|
||||
font->hhea_ptr.offset = offset;
|
||||
font->hhea_ptr.length = length;
|
||||
break;
|
||||
case 0x686d7478: /* hmtx */
|
||||
font->hmtx_ptr.offset = offset;
|
||||
font->hmtx_ptr.length = length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!font->head_ptr.offset) goto _fail_free;
|
||||
if (!font->glyf_ptr.offset) goto _fail_free;
|
||||
if (!font->cmap_ptr.offset) goto _fail_free;
|
||||
if (!font->loca_ptr.offset) goto _fail_free;
|
||||
|
||||
/* Try to pick a viable cmap */
|
||||
tt_seek(font, font->cmap_ptr.offset);
|
||||
|
||||
uint32_t best = 0;
|
||||
int bestScore = 0;
|
||||
|
||||
/* Read size */
|
||||
/* uint16_t cmap_vers = */ tt_read_16(font);
|
||||
uint16_t cmap_size = tt_read_16(font);
|
||||
for (unsigned int i = 0; i < cmap_size; ++i) {
|
||||
uint16_t platform = tt_read_16(font);
|
||||
uint16_t type = tt_read_16(font);
|
||||
uint32_t offset = tt_read_32(font);
|
||||
|
||||
if (platform == 3 && type == 10) {
|
||||
best = offset;
|
||||
bestScore = 4;
|
||||
} else if (platform == 0 && type == 4) {
|
||||
best = offset;
|
||||
bestScore = 4;
|
||||
} else if (platform == 0 && type == 3 && bestScore < 2) {
|
||||
best = offset;
|
||||
bestScore = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!best || bestScore != 4) {
|
||||
fprintf(stderr, "tt: TODO: unsupported cmap\n");
|
||||
goto _fail_free;
|
||||
}
|
||||
|
||||
/* What type is this */
|
||||
tt_seek(font, font->cmap_ptr.offset + best);
|
||||
|
||||
uint16_t cmap_type = tt_read_16(font);
|
||||
if (cmap_type != 12) {
|
||||
fprintf(stderr, "tt: TODO: unsupported cmap indexing\n");
|
||||
goto _fail_free;
|
||||
}
|
||||
|
||||
font->cmap_start = font->cmap_ptr.offset + best + 4;
|
||||
|
||||
return 1;
|
||||
|
||||
_fail_free:
|
||||
return 0;
|
||||
free(font);
|
||||
}
|
||||
|
||||
struct TT_Font * tt_font_from_file(const char * fileName) {
|
||||
FILE * f = fopen(fileName, "r");
|
||||
if (!f) return NULL;
|
||||
|
||||
struct TT_Font * font = calloc(sizeof(struct TT_Font), 1);
|
||||
font->filePtr = f;
|
||||
font->privFlags = 1;
|
||||
|
||||
if (!tt_font_load(font)) goto _fail_close;
|
||||
|
||||
return font;
|
||||
|
||||
_fail_close:
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct TT_Font * tt_font_from_memory(uint8_t * buffer) {
|
||||
struct TT_Font * font = calloc(sizeof(struct TT_Font), 1);
|
||||
font->privFlags = 0;
|
||||
font->buffer = buffer;
|
||||
if (!tt_font_load(font)) return NULL;
|
||||
return font;
|
||||
}
|
||||
|
||||
struct TT_Font * tt_font_from_file_mem(const char * fileName) {
|
||||
FILE * f = fopen(fileName, "r");
|
||||
if (!f) return NULL;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
uint8_t * buf = malloc(size);
|
||||
fread(buf, 1, size, f);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return tt_font_from_memory(buf);
|
||||
}
|
@ -32,6 +32,7 @@ class Classifier:
|
||||
'<toaru/menu.h>': (None, '-ltoaru_menu', ['<toaru/sdf.h>', '<toaru/yutani.h>', '<toaru/icon_cache.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
|
||||
'<toaru/textregion.h>': (None, '-ltoaru_textregion', ['<toaru/sdf.h>', '<toaru/yutani.h>','<toaru/graphics.h>', '<toaru/hashmap.h>']),
|
||||
'<toaru/button.h>': (None, '-ltoaru_button', ['<toaru/graphics.h>','<toaru/sdf.h>', '<toaru/icon_cache.h>']),
|
||||
'<toaru/text.h>': (None, '-ltoaru_text', ['<toaru/graphics.h>', '<toaru/hashmap.h>']),
|
||||
# Kuroko
|
||||
'<kuroko/kuroko.h>': ('../../../kuroko/src', '-lkuroko', []),
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user