colourpicker

Simple Colour Picker written in C
git clone anongit@rnpnr.xyz:colourpicker.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

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:
Mbuild.sh | 2+-
Mgen_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; }