Commit: 778ad6d9b572c1633809e6a48477939cdcf6aedd
Parent: ba8927b4546c26a0d9a350f387ff683708896be1
Author: Randy Palamar
Date: Wed, 31 Jul 2024 22:10:29 -0600
avoid opening a window for autogenerated files
This was fine on a local machine but doesn't work in CI when there
is no display server running.
Really something similar should be the default raylib interface.
Diffstat:
M | build.sh | | | 2 | +- |
M | gen_incs.c | | | 192 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
2 files changed, 170 insertions(+), 24 deletions(-)
diff --git a/build.sh b/build.sh
@@ -32,7 +32,7 @@ fi
[ ! -s "config.h" ] && cp config.def.h config.h
if [ ! -e "shader_inc.h" ] || [ "hsv_lerp.glsl" -nt "shader_inc.h" ]; then
- ${cc} $cflags -o gen_incs gen_incs.c $ldflags -flto -s
+ ${cc} $cflags -o gen_incs gen_incs.c $ldflags
./gen_incs
fi
diff --git a/gen_incs.c b/gen_incs.c
@@ -4,12 +4,38 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "config.h"
#define ISSPACE(a) ((a) == ' ' || (a) == '\t')
-typedef struct {int8_t *data; ptrdiff_t len;} s8;
+typedef struct {uint8_t *data; ptrdiff_t len;} s8;
+
+static s8
+read_whole_file(char *name)
+{
+ s8 res = {0};
+ FILE *fp = fopen(name, "r");
+
+ if (!fp) {
+ fputs("Failed to open file!\n", stdout);
+ exit(1);
+ }
+
+ fseek(fp, 0, SEEK_END);
+ res.len = ftell(fp);
+ rewind(fp);
+
+ res.data = malloc(res.len);
+ if (!res.data) {
+ fputs("Failed to allocate space for reading file!\n", stdout);
+ exit(1);
+ }
+ fread(res.data, res.len, 1, fp);
+ fclose(fp);
+ return res;
+}
static s8
get_line(s8 *s)
@@ -26,36 +52,157 @@ get_line(s8 *s)
return res;
}
-int
-main(void)
+static Font
+load_font(s8 font_data, Image *out, int font_size)
{
- SetConfigFlags(FLAG_WINDOW_HIDDEN);
- SetTraceLogLevel(LOG_NONE);
- InitWindow(640, 480, "");
- Font font = LoadFontEx("assets/Lora-SemiBold.ttf", FONT_SIZE, 0, 0);
- ExportFontAsCode(font, "font_inc.h");
- CloseWindow();
+ Font font = {0};
+ font.baseSize = font_size;
+ font.glyphCount = 95;
+ font.glyphPadding = 0;
+ font.glyphs = LoadFontData(font_data.data, font_data.len, font.baseSize, 0,
+ font.glyphCount, FONT_DEFAULT);
- FILE *shader_file, *out_file;
- shader_file = fopen(HSV_LERP_SHADER_NAME, "r");
- out_file = fopen("shader_inc.h", "w");
+ if (font.glyphs != NULL) {
+ font.glyphPadding = 4;
+ *out = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount,
+ font.baseSize, font.glyphPadding, 0);
- if (!shader_file || !out_file) {
- fputs("Failed to open necessary files!\n", stdout);
- return 1;
+ // Update glyphs[i].image to use alpha, required to be used on ImageDrawText()
+ for (int i = 0; i < font.glyphCount; i++) {
+ UnloadImage(font.glyphs[i].image);
+ font.glyphs[i].image = ImageFromImage(*out, font.recs[i]);
+ }
}
+ return font;
+}
+
+/* NOTE: Modified from raylib. Used to save font data without opening a window */
+static void
+export_font_as_code(Font font, Image image, const char *fileName)
+{
+ #define TEXT_BYTES_PER_LINE 20
+ #define MAX_FONT_DATA_SIZE 1024*1024 // 1 MB
+
+ // Get file name from path
+ char fileNamePascal[256] = { 0 };
+ strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1);
- fseek(shader_file, 0, SEEK_END);
- s8 shader_data = {.len = ftell(shader_file)};
- rewind(shader_file);
+ // NOTE: Text data buffer size is estimated considering image data size in bytes
+ // and requiring 6 char bytes for every byte: "0x00, "
+ char *txt = calloc(MAX_FONT_DATA_SIZE, 1);
- shader_data.data = malloc(shader_data.len);
- if (!shader_data.data) {
- fputs("Failed to allocate space for reading shader file!\n", stdout);
+ int off = 0;
+ off += sprintf(txt + off, "////////////////////////////////////////////////////////////////////////////////////////\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "// FontAsCode exporter v1.0 - Font data exported as an array of bytes //\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "// more info and bugs-report: github.com/raysan5/raylib //\n");
+ off += sprintf(txt + off, "// feedback and support: ray[at]raylib.com //\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "// ---------------------------------------------------------------------------------- //\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "// TODO: Fill the information and license of the exported font here: //\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "// Font name: .... //\n");
+ off += sprintf(txt + off, "// Font creator: .... //\n");
+ off += sprintf(txt + off, "// Font LICENSE: .... //\n");
+ off += sprintf(txt + off, "// //\n");
+ off += sprintf(txt + off, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
+
+ // Support font export and initialization
+ // NOTE: This mechanism is highly coupled to raylib
+ int imageDataSize = GetPixelDataSize(image.width, image.height, image.format);
+
+ // Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE
+ //ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
+
+ // WARNING: Data is compressed using raylib CompressData() DEFLATE,
+ // it requires to be decompressed with raylib DecompressData(), that requires
+ // compiling raylib with SUPPORT_COMPRESSION_API config flag enabled
+
+ // Compress font image data
+ int compDataSize = 0;
+ unsigned char *compData = CompressData((const unsigned char *)image.data, imageDataSize, &compDataSize);
+
+ // Save font image data (compressed)
+ off += sprintf(txt + off, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(fileNamePascal), compDataSize);
+ off += sprintf(txt + off, "// Font image pixels data compressed (DEFLATE)\n");
+ off += sprintf(txt + off, "// NOTE: Original pixel data simplified to GRAYSCALE\n");
+ off += sprintf(txt + off, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal));
+ for (int i = 0; i < compDataSize - 1; i++) off += sprintf(txt + off, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), compData[i]);
+ off += sprintf(txt + off, "0x%02x };\n\n", compData[compDataSize - 1]);
+ RL_FREE(compData);
+
+ // Save font recs data
+ off += sprintf(txt + off, "// Font characters rectangles data\n");
+ off += sprintf(txt + off, "static Rectangle fontRecs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
+ for (int i = 0; i < font.glyphCount; i++) {
+ off += sprintf(txt + off, " { %1.0f, %1.0f, %1.0f , %1.0f },\n",
+ font.recs[i].x, font.recs[i].y, font.recs[i].width, font.recs[i].height);
+ }
+ off += sprintf(txt + off, "};\n\n");
+
+ // Save font glyphs data
+ // NOTE: Glyphs image data not saved (grayscale pixels),
+ // it could be generated from image and recs
+ off += sprintf(txt + off, "// Font glyphs info data\n");
+ off += sprintf(txt + off, "// NOTE: No glyphs.image data provided\n");
+ off += sprintf(txt + off, "static GlyphInfo fontGlyphs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
+ for (int i = 0; i < font.glyphCount; i++) {
+ off += sprintf(txt + off, " { %i, %i, %i, %i, { 0 }},\n",
+ font.glyphs[i].value, font.glyphs[i].offsetX,
+ font.glyphs[i].offsetY, font.glyphs[i].advanceX);
+ }
+ off += sprintf(txt + off, "};\n\n");
+
+ // Custom font loading function
+ off += sprintf(txt + off, "// Font loading function: %s\n", fileNamePascal);
+ off += sprintf(txt + off, "static Font LoadFont_%s(void)\n{\n", fileNamePascal);
+ off += sprintf(txt + off, " Font font = { 0 };\n\n");
+ off += sprintf(txt + off, " font.baseSize = %i;\n", font.baseSize);
+ off += sprintf(txt + off, " font.glyphCount = %i;\n", font.glyphCount);
+ off += sprintf(txt + off, " font.glyphPadding = %i;\n\n", font.glyphPadding);
+ off += sprintf(txt + off, " // Custom font loading\n");
+ off += sprintf(txt + off, " // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function\n");
+ off += sprintf(txt + off, " int fontDataSize_%s = 0;\n", fileNamePascal);
+ off += sprintf(txt + off, " unsigned char *data = DecompressData(fontData_%s, COMPRESSED_DATA_SIZE_FONT_%s, &fontDataSize_%s);\n", fileNamePascal, TextToUpper(fileNamePascal), fileNamePascal);
+ off += sprintf(txt + off, " Image imFont = { data, %i, %i, 1, %i };\n\n", image.width, image.height, image.format);
+ off += sprintf(txt + off, " // Load texture from image\n");
+ off += sprintf(txt + off, " font.texture = LoadTextureFromImage(imFont);\n");
+ off += sprintf(txt + off, " UnloadImage(imFont); // Uncompressed data can be unloaded from memory\n\n");
+ // We have two possible mechanisms to assign font.recs and font.glyphs data,
+ // that data is already available as global arrays, we two options to assign that data:
+ // - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code
+ // - 2. Data assignment. This option consumes less memory and Font MUST NOT be unloaded by user because data is on protected DATA segment
+ off += sprintf(txt + off, " // Assign glyph recs and info data directly\n");
+ off += sprintf(txt + off, " // WARNING: This font data must not be unloaded\n");
+ off += sprintf(txt + off, " font.recs = fontRecs_%s;\n", fileNamePascal);
+ off += sprintf(txt + off, " font.glyphs = fontGlyphs_%s;\n\n", fileNamePascal);
+ off += sprintf(txt + off, " return font;\n");
+ off += sprintf(txt + off, "}\n");
+
+ // NOTE: Text data size exported is determined by '\0' (NULL) character
+ SaveFileText(fileName, txt);
+}
+
+int
+main(void)
+{
+ SetTraceLogLevel(LOG_NONE);
+ Image atlas;
+ s8 font_data = read_whole_file("assets/Lora-SemiBold.ttf");
+ Font font = load_font(font_data, &atlas, FONT_SIZE);
+ export_font_as_code(font, atlas, "font_inc.h");
+
+ FILE *out_file = fopen("shader_inc.h", "w");
+ if (!out_file) {
+ fputs("Failed to open necessary files!\n", stdout);
return 1;
}
- fread(shader_data.data, shader_data.len, 1, shader_file);
+ s8 shader_data = read_whole_file(HSV_LERP_SHADER_NAME);
s8 s = shader_data;
/* NOTE: skip over license notice */
s8 line = get_line(&s);
@@ -74,7 +221,6 @@ main(void)
} while (s.len > 0);
fputs(";\n", out_file);
fclose(out_file);
- fclose(shader_file);
return 0;
}