mirror of
https://git.sr.ht/~calebccff/pbsplash
synced 2026-01-13 04:58:39 -09:00
wip svg font rendering
works with built in draw_svg stuff, trying to support nsvgrast
This commit is contained in:
parent
2844142e1b
commit
1d29b4d87a
5 changed files with 1702 additions and 49 deletions
15
.vscode/settings.json
vendored
Normal file
15
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"array": "c",
|
||||||
|
"hash_map": "c",
|
||||||
|
"deque": "c",
|
||||||
|
"list": "c",
|
||||||
|
"string": "c",
|
||||||
|
"unordered_map": "c",
|
||||||
|
"unordered_set": "c",
|
||||||
|
"vector": "c",
|
||||||
|
"string_view": "c",
|
||||||
|
"initializer_list": "c",
|
||||||
|
"valarray": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
const float logo[] = {
|
|
||||||
// Top
|
|
||||||
50, 00, 35, 30, // top to left
|
|
||||||
35, 30, 38, 42, // top left point left
|
|
||||||
38, 42, 50, 40, // top left point right
|
|
||||||
50, 40, 67, 75, // top to right inside
|
|
||||||
67, 75, 70, 63,
|
|
||||||
70, 63, 83, 65,
|
|
||||||
83, 65, 50, 00, // top to right
|
|
||||||
// Bottom left
|
|
||||||
32, 35, 0, 100,
|
|
||||||
0, 100, 30, 100,
|
|
||||||
30, 100, 35, 90,
|
|
||||||
35, 90, 30, 80,
|
|
||||||
30, 80, 45, 45,
|
|
||||||
45, 45, 35, 47,
|
|
||||||
35, 47, 32, 35,
|
|
||||||
// Bottom right
|
|
||||||
35, 80, 40, 90,
|
|
||||||
40, 90, 35, 100,
|
|
||||||
35, 100, 100, 100,
|
|
||||||
100, 100, 85, 70,
|
|
||||||
85, 70, 73, 68,
|
|
||||||
73, 68, 70, 80,
|
|
||||||
70, 80, 35, 80,
|
|
||||||
};
|
|
||||||
|
|
@ -151,6 +151,8 @@ typedef struct NSVGshape
|
||||||
char fillRule; // Fill rule, see NSVGfillRule.
|
char fillRule; // Fill rule, see NSVGfillRule.
|
||||||
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
unsigned char flags; // Logical or of NSVG_FLAGS_* flags
|
||||||
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
|
||||||
|
char *unicode; // Unicode character code.
|
||||||
|
int horizAdvX; // Horizontal distance to advance after rendering glyph.
|
||||||
NSVGpath* paths; // Linked list of paths in the image.
|
NSVGpath* paths; // Linked list of paths in the image.
|
||||||
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
struct NSVGshape* next; // Pointer to next shape, or NULL if last element.
|
||||||
} NSVGshape;
|
} NSVGshape;
|
||||||
|
|
@ -452,6 +454,9 @@ typedef struct NSVGparser
|
||||||
float dpi;
|
float dpi;
|
||||||
char pathFlag;
|
char pathFlag;
|
||||||
char defsFlag;
|
char defsFlag;
|
||||||
|
char *unicodeFlag;
|
||||||
|
char *horizAdvFlag;
|
||||||
|
char *defaultHorizAdv;
|
||||||
} NSVGparser;
|
} NSVGparser;
|
||||||
|
|
||||||
static void nsvg__xformIdentity(float* t)
|
static void nsvg__xformIdentity(float* t)
|
||||||
|
|
@ -945,6 +950,7 @@ static void nsvg__addShape(NSVGparser* p)
|
||||||
NSVGattrib* attr = nsvg__getAttr(p);
|
NSVGattrib* attr = nsvg__getAttr(p);
|
||||||
float scale = 1.0f;
|
float scale = 1.0f;
|
||||||
NSVGshape* shape;
|
NSVGshape* shape;
|
||||||
|
char *end;
|
||||||
NSVGpath* path;
|
NSVGpath* path;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
@ -968,6 +974,20 @@ static void nsvg__addShape(NSVGparser* p)
|
||||||
shape->fillRule = attr->fillRule;
|
shape->fillRule = attr->fillRule;
|
||||||
shape->opacity = attr->opacity;
|
shape->opacity = attr->opacity;
|
||||||
|
|
||||||
|
if (p->unicodeFlag) {
|
||||||
|
shape->unicode = p->unicodeFlag;
|
||||||
|
if (p->horizAdvFlag) {
|
||||||
|
shape->horizAdvX = strtol(p->horizAdvFlag, &end, 10);
|
||||||
|
if (end == p->horizAdvFlag)
|
||||||
|
shape->horizAdvX = 0;
|
||||||
|
}
|
||||||
|
if (shape->horizAdvX == 0) {
|
||||||
|
shape->horizAdvX = p->defaultHorizAdv;
|
||||||
|
}
|
||||||
|
p->unicodeFlag = NULL;
|
||||||
|
p->horizAdvFlag = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
shape->paths = p->plist;
|
shape->paths = p->plist;
|
||||||
p->plist = NULL;
|
p->plist = NULL;
|
||||||
|
|
||||||
|
|
@ -1854,12 +1874,19 @@ static void nsvg__parseStyle(NSVGparser* p, const char* str)
|
||||||
static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
|
static void nsvg__parseAttribs(NSVGparser* p, const char** attr)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
char *end;
|
||||||
for (i = 0; attr[i]; i += 2)
|
for (i = 0; attr[i]; i += 2)
|
||||||
{
|
{
|
||||||
if (strcmp(attr[i], "style") == 0)
|
if (strcmp(attr[i], "style") == 0) {
|
||||||
nsvg__parseStyle(p, attr[i + 1]);
|
nsvg__parseStyle(p, attr[i + 1]);
|
||||||
else
|
} else if (strcmp(attr[i], "horiz-adv-x") == 0) {
|
||||||
|
printf("Setting defualt horiz adv to %s\n", attr[i + 1]);
|
||||||
|
p->defaultHorizAdv = strtol(attr[i+1], &end, 10);
|
||||||
|
if (end == p->defaultHorizAdv)
|
||||||
|
p->defaultHorizAdv = 0;
|
||||||
|
} else {
|
||||||
nsvg__parseAttr(p, attr[i], attr[i + 1]);
|
nsvg__parseAttr(p, attr[i], attr[i + 1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2218,6 +2245,11 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr)
|
||||||
for (i = 0; attr[i]; i += 2) {
|
for (i = 0; attr[i]; i += 2) {
|
||||||
if (strcmp(attr[i], "d") == 0) {
|
if (strcmp(attr[i], "d") == 0) {
|
||||||
s = attr[i + 1];
|
s = attr[i + 1];
|
||||||
|
} else if (strcmp(attr[i], "unicode") == 0) {
|
||||||
|
p->unicodeFlag = malloc(strlen(attr[i+1]));
|
||||||
|
strcpy(p->unicodeFlag, attr[i+1]);
|
||||||
|
} else if (strcmp(attr[i], "horiz-adv-x") == 0) {
|
||||||
|
p->horizAdvFlag = attr[i+1];
|
||||||
} else {
|
} else {
|
||||||
tmp[0] = attr[i];
|
tmp[0] = attr[i];
|
||||||
tmp[1] = attr[i + 1];
|
tmp[1] = attr[i + 1];
|
||||||
|
|
@ -2698,14 +2730,27 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr)
|
||||||
NSVGparser* p = (NSVGparser*)ud;
|
NSVGparser* p = (NSVGparser*)ud;
|
||||||
|
|
||||||
if (p->defsFlag) {
|
if (p->defsFlag) {
|
||||||
// Skip everything but gradients in defs
|
// Skip everything but gradients and fonts in defs
|
||||||
if (strcmp(el, "linearGradient") == 0) {
|
if (strcmp(el, "linearGradient") == 0) {
|
||||||
nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
|
nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
|
||||||
} else if (strcmp(el, "radialGradient") == 0) {
|
} else if (strcmp(el, "radialGradient") == 0) {
|
||||||
nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
|
nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
|
||||||
} else if (strcmp(el, "stop") == 0) {
|
} else if (strcmp(el, "stop") == 0) {
|
||||||
nsvg__parseGradientStop(p, attr);
|
nsvg__parseGradientStop(p, attr);
|
||||||
|
} else if (strcmp(el, "glyph") == 0) { // glyphs are just special paths
|
||||||
|
if (p->pathFlag) // Do not allow nested paths.
|
||||||
|
return;
|
||||||
|
nsvg__pushAttr(p);
|
||||||
|
nsvg__parsePath(p, attr);
|
||||||
|
nsvg__popAttr(p);
|
||||||
|
} else if (strcmp(el, "font") == 0) { // fonts are special "g" tags
|
||||||
|
nsvg__pushAttr(p);
|
||||||
|
nsvg__parseAttribs(p, attr);
|
||||||
}
|
}
|
||||||
|
// } else if (strcmp(el, "missing-glyph") == 0) { // for the default character width
|
||||||
|
// nsvg__pushAttr(p);
|
||||||
|
// nsvg__parseAttribs(p, attr);
|
||||||
|
// }
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2906,6 +2951,30 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSVGshape** nsvgGetTextShapes(NSVGimage* image, char* text, int textLen)
|
||||||
|
{
|
||||||
|
NSVGshape *shape = NULL;
|
||||||
|
NSVGpath *path = NULL;
|
||||||
|
NSVGshape **shapes = malloc(sizeof(NSVGshape*)*textLen); // array of paths, text to render
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// make list of paths representing glyphs to render
|
||||||
|
for (i = 0; i < textLen; i++)
|
||||||
|
{
|
||||||
|
for (shape = image->shapes; shape != NULL; shape = shape->next) {
|
||||||
|
if (!(shape->flags & NSVG_FLAGS_VISIBLE))
|
||||||
|
continue;
|
||||||
|
if (shape->unicode)
|
||||||
|
if (shape->unicode[0] == text[i]) {
|
||||||
|
shapes[i] = shape;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shapes;
|
||||||
|
}
|
||||||
|
|
||||||
NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
NSVGimage* nsvgParse(char* input, const char* units, float dpi)
|
||||||
{
|
{
|
||||||
NSVGparser* p;
|
NSVGparser* p;
|
||||||
|
|
|
||||||
1541
include/nanosvgrast.h
Normal file
1541
include/nanosvgrast.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <tfblib/tfblib.h>
|
#include <tfblib/tfblib.h>
|
||||||
#include <tfblib/tfb_colors.h>
|
#include <tfblib/tfb_colors.h>
|
||||||
|
|
||||||
|
|
@ -9,10 +11,11 @@
|
||||||
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
|
#define NANOSVG_ALL_COLOR_KEYWORDS // Include full list of color keywords.
|
||||||
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
#define NANOSVG_IMPLEMENTATION // Expands implementation
|
||||||
#include "nanosvg.h"
|
#include "nanosvg.h"
|
||||||
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
#include "logo.h"
|
#include "nanosvgrast.h"
|
||||||
|
|
||||||
#define MSG_MAX_LEN 4096
|
#define MSG_MAX_LEN 4096
|
||||||
|
#define FONT_PATH "/usr/share/pbsplash/OpenSans-Regular.svg"
|
||||||
|
|
||||||
int usage()
|
int usage()
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +59,7 @@ static void vertex2f(float x, float y, bool reset)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!first)
|
if (!first)
|
||||||
tfb_draw_line(x, y, x1, y1, tfb_green);
|
tfb_draw_line(x1, y1, x, y, tfb_green);
|
||||||
else
|
else
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
|
|
@ -104,36 +107,70 @@ static void cubicBez(float x1, float y1, float x2, float y2,
|
||||||
static void drawPath(float *pts, int npts, int xoff, int yoff, float scale_x, float scale_y, char closed, float tol)
|
static void drawPath(float *pts, int npts, int xoff, int yoff, float scale_x, float scale_y, char closed, float tol)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
vertex2f(xoff + pts[0] * scale_x, yoff + pts[1] * scale_y, true);
|
vertex2f(xoff + pts[0] * scale_x, yoff - pts[1] * scale_y, true);
|
||||||
for (i = 0; i < npts-1; i += 3) {
|
for (i = 0; i < npts-1; i += 3) {
|
||||||
float* p = &pts[i*2];
|
float* p = &pts[i*2];
|
||||||
// fprintf(stdout, "ptx0: %f, pty0: %f, ptx1: %f, pty1: %f, ptx2: %f, pty2: %f, ptx3: %f, pty3: %f\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
// fprintf(stdout, "ptx0: %f, pty0: %f, ptx1: %f, pty1: %f, ptx2: %f, pty2: %f, ptx3: %f, pty3: %f\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
||||||
cubicBez(xoff + p[0] * scale_x, yoff + p[1] * scale_y, xoff + p[2] * scale_x, yoff + p[3] * scale_y, xoff + p[4] * scale_x, yoff + p[5] * scale_y, xoff + p[6] * scale_x, yoff + p[7] * scale_y, tol, 0);
|
cubicBez(xoff + p[0] * scale_x, yoff - p[1] * scale_y, xoff + p[2] * scale_x, yoff - p[3] * scale_y, xoff + p[4] * scale_x, yoff - p[5] * scale_y, xoff + p[6] * scale_x, yoff - p[7] * scale_y, tol, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closed) {
|
if (closed) {
|
||||||
vertex2f(xoff + pts[0] * scale_x, yoff + pts[1] * scale_y, false);
|
vertex2f(xoff + pts[0] * scale_x, yoff - pts[1] * scale_y, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blit an SVG to the framebuffer using tfblib. The image is
|
// Blit an SVG to the framebuffer using tfblib. The image is
|
||||||
// scaled based on the target width and height.
|
// scaled based on the target width and height.
|
||||||
static void blit_svg(struct NSVGimage *image, int x, int y, int width, int height)
|
static void draw_svg(NSVGimage *image, int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
struct NSVGshape *shape;
|
|
||||||
struct NSVGpath *path;
|
|
||||||
fprintf(stdout, "size: %f x %f\n", image->width, image->height);
|
fprintf(stdout, "size: %f x %f\n", image->width, image->height);
|
||||||
float sz_x = (float)width / image->width;
|
float sz = (float)width / image->width;
|
||||||
float sz_y = (float)height / image->height;
|
int w = image->width * sz + 0.5;
|
||||||
fprintf(stdout, "scale: %f x %f\n", sz_x, sz_y);
|
int h = image->height * sz + 0.5;
|
||||||
|
fprintf(stdout, "scale: %f\n", sz);
|
||||||
|
|
||||||
for (shape = image->shapes; shape != NULL; shape = shape->next)
|
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||||
|
unsigned char *img = malloc(w * h * 4);
|
||||||
|
nsvgRasterize(rast, image, 0, 0, sz, img, w, h, w * 4);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < w; i++)
|
||||||
{
|
{
|
||||||
for (path = shape->paths; path != NULL; path = path->next)
|
for (size_t j = 0; j < h; j++)
|
||||||
{
|
{
|
||||||
drawPath(path->pts, path->npts, x, y, sz_x, sz_y, path->closed, 0.1f);
|
unsigned int col = tfb_make_color(img[(j * w + i) * 4 + 0], img[(j * w + i) * 4 + 1], img[(j * w + i) * 4 + 2]);
|
||||||
|
tfb_draw_pixel(x + i, y + j, col);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_text(NSVGimage *font, char *text, int x, int y, float scale)
|
||||||
|
{
|
||||||
|
int width, height = 200;
|
||||||
|
printf("text: %s\n", text);
|
||||||
|
NSVGshape **shapes = nsvgGetTextShapes(font, text, strlen(text));
|
||||||
|
for (size_t i = 0; i < strlen(text); i++)
|
||||||
|
{
|
||||||
|
width += shapes[i]->horizAdvX * scale + 1;
|
||||||
|
}
|
||||||
|
unsigned char *img = malloc(width * height * 4);
|
||||||
|
NSVGrasterizer *rast = nsvgCreateRasterizer();
|
||||||
|
|
||||||
|
printf("text dimensions: %d x %d\n", width, height);
|
||||||
|
|
||||||
|
nsvgRasterize(rast, font, 0, 0, scale, img, width, height, width * 4);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < width; i++)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < height; j++)
|
||||||
|
{
|
||||||
|
unsigned int col = tfb_make_color(img[(j * width + i) * 4 + 0], img[(j * width + i) * 4 + 1], img[(j * width + i) * 4 + 2]);
|
||||||
|
tfb_draw_pixel(x + i, y - j, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
|
|
@ -142,7 +179,8 @@ int main(int argc, char **argv)
|
||||||
int opt;
|
int opt;
|
||||||
char *message = calloc(MSG_MAX_LEN, 1);
|
char *message = calloc(MSG_MAX_LEN, 1);
|
||||||
char *splash_image;
|
char *splash_image;
|
||||||
struct NSVGimage *image;
|
NSVGimage *image;
|
||||||
|
NSVGimage *font;
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -153,7 +191,7 @@ int main(int argc, char **argv)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(argv[i], "-s") == 0)
|
if (strcmp(argv[i], "-s") == 0 && i + 1 < argc)
|
||||||
{
|
{
|
||||||
splash_image = argv[i + 1];
|
splash_image = argv[i + 1];
|
||||||
printf("splash_image path: %s\n", splash_image);
|
printf("splash_image path: %s\n", splash_image);
|
||||||
|
|
@ -188,17 +226,31 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
int w = (int)tfb_screen_width();
|
int w = (int)tfb_screen_width();
|
||||||
int h = (int)tfb_screen_height();
|
int h = (int)tfb_screen_height();
|
||||||
float sz = (float)(w < h ? w : h) * 0.6f;
|
float sz = (float)(w < h ? w : h) * 0.5f;
|
||||||
fprintf(stdout, "w=%du, h=%du\n", w, h);
|
fprintf(stdout, "w=%du, h=%du\n", w, h);
|
||||||
float x = (float)w / 2 - sz / 2;
|
float x = (float)w / 2 - sz / 2;
|
||||||
float y = (float)h / 2 - sz / 2 - 300;
|
float y = (float)h / 2 - sz / 2 - 300;
|
||||||
fprintf(stdout, "x=%f, y=%f, sz=%f\n", x, y, sz);
|
fprintf(stdout, "x=%f, y=%f, sz=%f\n", x, y, sz);
|
||||||
|
|
||||||
image = nsvgParseFromFile(splash_image, "px", 96);
|
image = nsvgParseFromFile(splash_image, "px", 96);
|
||||||
|
if (!image)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "failed to load SVG image\n");
|
||||||
|
rc = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
font = nsvgParseFromFile(FONT_PATH, "px", 500);
|
||||||
|
if (!font || !font->shapes)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "failed to load SVG font\n");
|
||||||
|
rc = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
float fontsz = 0.05f;
|
||||||
|
fprintf(stdout, "font size: %f\n", fontsz);
|
||||||
|
|
||||||
/* Paint the whole screen in black */
|
/* Paint the whole screen in black */
|
||||||
tfb_clear_screen(tfb_black);
|
tfb_clear_screen(tfb_black);
|
||||||
/* Draw a red rectangle at the center of the screen */
|
|
||||||
/* Draw some text on-screen */
|
/* Draw some text on-screen */
|
||||||
tfb_draw_string_scaled(x + 75, y + sz + 40, tfb_white, tfb_black, 4, 4, "postmarketOS");
|
tfb_draw_string_scaled(x + 75, y + sz + 40, tfb_white, tfb_black, 4, 4, "postmarketOS");
|
||||||
|
|
||||||
|
|
@ -207,7 +259,9 @@ int main(int argc, char **argv)
|
||||||
tfb_draw_string_scaled_wrapped(50, (int)((float)h * 0.6), tfb_white, tfb_black, 2, 2, 80, message);
|
tfb_draw_string_scaled_wrapped(50, (int)((float)h * 0.6), tfb_white, tfb_black, 2, 2, 80, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
blit_svg(image, x, y, sz, sz);
|
draw_svg(image, x, y, sz, sz);
|
||||||
|
|
||||||
|
draw_text(font, "postmarketOS", 100, h / 2.f + 100, fontsz);
|
||||||
|
|
||||||
tfb_flush_window();
|
tfb_flush_window();
|
||||||
tfb_flush_fb();
|
tfb_flush_fb();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue