3131#include < memory>
3232#include < array>
3333#include < chrono>
34+ #include < cmath>
35+ #include < cstring>
36+ #include < filesystem>
37+ #include < string>
3438#include < glad/glad.h>
3539#include < glad/glad.c>
3640#include < gl/glu.h>
4953#define VE_FONTCACHE_IMPL
5054// #define VE_FONTCACHE_DEBUGPRINT
5155#include " ../ve_fontcache.h"
56+ #include " ../ve_fontcache_backend_test.h"
5257
5358static ve_fontcache cache;
5459
@@ -63,6 +68,42 @@ static std::vector< GLuint > fonecache_CPU_atlas_textures; // Used with VE_FONTC
6368TinyWindow::vec2_t < unsigned int > window_size;
6469static int mouse_scroll = 0 ;
6570
71+ static std::filesystem::path current_executable_directory ()
72+ {
73+ std::array< char , MAX_PATH > path {};
74+ DWORD length = GetModuleFileNameA ( nullptr , path.data (), static_cast < DWORD >( path.size () ) );
75+ return std::filesystem::path ( std::string ( path.data (), length ) ).parent_path ();
76+ }
77+
78+ static std::string resolve_demo_asset_path ( const char * relative_path )
79+ {
80+ const std::filesystem::path relative ( relative_path );
81+ const std::filesystem::path cwd = std::filesystem::current_path ();
82+ const std::filesystem::path exe_dir = current_executable_directory ();
83+ const std::array< std::filesystem::path, 6 > candidates = {
84+ cwd / relative,
85+ cwd / " demo" / relative,
86+ cwd / " .." / " demo" / relative,
87+ exe_dir / relative,
88+ exe_dir / " .." / " demo" / relative,
89+ exe_dir / " .." / " .." / " demo" / relative,
90+ };
91+
92+ for ( const std::filesystem::path& candidate : candidates ) {
93+ if ( std::filesystem::exists ( candidate ) ) {
94+ return candidate.lexically_normal ().string ();
95+ }
96+ }
97+
98+ return relative.string ();
99+ }
100+
101+ static ve_font_id load_demo_font ( ve_fontcache* target_cache, const char * relative_path, std::vector< uint8_t >& buffer, float size_px )
102+ {
103+ std::string resolved_path = resolve_demo_asset_path ( relative_path );
104+ return ve_fontcache_loadfile ( target_cache, resolved_path.c_str (), buffer, size_px );
105+ }
106+
66107const std::string vs_source_shared = R"(
67108#version 330 core
68109in vec2 vpos;
@@ -422,27 +463,27 @@ void init_demo()
422463 static std::vector< uint8_t > buffer, buffer2, buffer3, buffer4, buffer5, buffer6,
423464 buffer7, buffer8, buffer9, buffer10, buffer11, buffer12, buffer13, buffer14;
424465
425- logo_font = ve_fontcache_loadfile ( &cache, " fonts/SawarabiMincho-Regular.ttf" , buffer, 330 .0f );
426- title_font = ve_fontcache_loadfile ( &cache, " fonts/OpenSans-Regular.ttf" , buffer2, 42 .0f );
427- print_font = ve_fontcache_loadfile ( &cache, " fonts/NotoSansJP-Light.otf" , buffer3, 19 .0f );
428- mono_font = ve_fontcache_loadfile ( &cache, " fonts/UbuntuMono-Regular.ttf" , buffer4, 21 .0f );
429- small_font = ve_fontcache_loadfile ( &cache, " fonts/Roboto-Regular.ttf" , buffer14, 10 .0f );
430-
431- demo_sans_font = ve_fontcache_loadfile ( &cache, " fonts/OpenSans-Regular.ttf" , buffer2, 18 .0f );
432- demo_serif_font = ve_fontcache_loadfile ( &cache, " fonts/Bitter-Regular.ttf" , buffer5, 18 .0f );
433- demo_script_font = ve_fontcache_loadfile ( &cache, " fonts/DancingScript-Regular.ttf" , buffer6, 22 .0f );
434- demo_mono_font = ve_fontcache_loadfile ( &cache, " fonts/NovaMono-Regular.ttf" , buffer7, 18 .0f );
435-
436- demo_chinese_font = ve_fontcache_loadfile ( &cache, " fonts/NotoSerifSC-Regular.otf" , buffer8, 24 .0f );
437- demo_japanese_font = ve_fontcache_loadfile ( &cache, " fonts/SawarabiMincho-Regular.ttf" , buffer, 24 .0f );
438- demo_korean_font = ve_fontcache_loadfile ( &cache, " fonts/NanumPenScript-Regular.ttf" , buffer9, 36 .0f );
439- demo_thai_font = ve_fontcache_loadfile ( &cache, " fonts/Krub-Regular.ttf" , buffer10, 24 .0f );
440- demo_arabic_font = ve_fontcache_loadfile ( &cache, " fonts/Tajawal-Regular.ttf" , buffer11, 24 .0f );
441- demo_hebrew_font = ve_fontcache_loadfile ( &cache, " fonts/DavidLibre-Regular.ttf" , buffer12, 22 .0f );
442-
443- demo_raincode_font = ve_fontcache_loadfile ( &cache, " fonts/NotoSansJP-Regular.otf" , buffer13, 20 .0f );
444- demo_grid2_font = ve_fontcache_loadfile ( &cache, " fonts/NotoSerifSC-Regular.otf" , buffer8, 54 .0f );
445- demo_grid3_font = ve_fontcache_loadfile ( &cache, " fonts/Bitter-Regular.ttf" , buffer5, 44 .0f );
466+ logo_font = load_demo_font ( &cache, " fonts/SawarabiMincho-Regular.ttf" , buffer, 330 .0f );
467+ title_font = load_demo_font ( &cache, " fonts/OpenSans-Regular.ttf" , buffer2, 42 .0f );
468+ print_font = load_demo_font ( &cache, " fonts/NotoSansJP-Light.otf" , buffer3, 19 .0f );
469+ mono_font = load_demo_font ( &cache, " fonts/UbuntuMono-Regular.ttf" , buffer4, 21 .0f );
470+ small_font = load_demo_font ( &cache, " fonts/Roboto-Regular.ttf" , buffer14, 10 .0f );
471+
472+ demo_sans_font = load_demo_font ( &cache, " fonts/OpenSans-Regular.ttf" , buffer2, 18 .0f );
473+ demo_serif_font = load_demo_font ( &cache, " fonts/Bitter-Regular.ttf" , buffer5, 18 .0f );
474+ demo_script_font = load_demo_font ( &cache, " fonts/DancingScript-Regular.ttf" , buffer6, 22 .0f );
475+ demo_mono_font = load_demo_font ( &cache, " fonts/NovaMono-Regular.ttf" , buffer7, 18 .0f );
476+
477+ demo_chinese_font = load_demo_font ( &cache, " fonts/NotoSerifSC-Regular.otf" , buffer8, 24 .0f );
478+ demo_japanese_font = load_demo_font ( &cache, " fonts/SawarabiMincho-Regular.ttf" , buffer, 24 .0f );
479+ demo_korean_font = load_demo_font ( &cache, " fonts/NanumPenScript-Regular.ttf" , buffer9, 36 .0f );
480+ demo_thai_font = load_demo_font ( &cache, " fonts/Krub-Regular.ttf" , buffer10, 24 .0f );
481+ demo_arabic_font = load_demo_font ( &cache, " fonts/Tajawal-Regular.ttf" , buffer11, 24 .0f );
482+ demo_hebrew_font = load_demo_font ( &cache, " fonts/DavidLibre-Regular.ttf" , buffer12, 22 .0f );
483+
484+ demo_raincode_font = load_demo_font ( &cache, " fonts/NotoSansJP-Regular.otf" , buffer13, 20 .0f );
485+ demo_grid2_font = load_demo_font ( &cache, " fonts/NotoSerifSC-Regular.otf" , buffer8, 54 .0f );
486+ demo_grid3_font = load_demo_font ( &cache, " fonts/Bitter-Regular.ttf" , buffer5, 44 .0f );
446487}
447488
448489void render_demo ( TinyWindow::tWindow* window, float dT )
@@ -777,7 +818,144 @@ void test_plist()
777818 }
778819}
779820
780- int main ()
821+ static bool has_flag ( int argc, char ** argv, const char * flag )
822+ {
823+ for ( int i = 1 ; i < argc; i++ ) {
824+ if ( std::strcmp ( argv[ i ], flag ) == 0 ) {
825+ return true ;
826+ }
827+ }
828+
829+ return false ;
830+ }
831+
832+ static void clear_framebuffer_colour ( GLuint framebuffer )
833+ {
834+ glBindFramebuffer ( GL_FRAMEBUFFER, framebuffer );
835+ glDisable ( GL_FRAMEBUFFER_SRGB );
836+ glViewport (
837+ 0 ,
838+ 0 ,
839+ framebuffer == 0 ? static_cast < GLsizei >( window_size.width ) : ( framebuffer == fontcache_fbo[ 0 ] ? VE_FONTCACHE_GLYPHDRAW_BUFFER_WIDTH : VE_FONTCACHE_ATLAS_WIDTH ),
840+ framebuffer == 0 ? static_cast < GLsizei >( window_size.height ) : ( framebuffer == fontcache_fbo[ 0 ] ? VE_FONTCACHE_GLYPHDRAW_BUFFER_HEIGHT : VE_FONTCACHE_ATLAS_HEIGHT ) );
841+ glClearColor ( 0 .0f , 0 .0f , 0 .0f , 0 .0f );
842+ glClear ( GL_COLOR_BUFFER_BIT );
843+ }
844+
845+ static void clear_backend_test_surfaces ()
846+ {
847+ clear_framebuffer_colour ( fontcache_fbo[ 0 ] );
848+ clear_framebuffer_colour ( fontcache_fbo[ 1 ] );
849+ clear_framebuffer_colour ( 0 );
850+ glBindFramebuffer ( GL_FRAMEBUFFER, 0 );
851+ }
852+
853+ static bool backend_test_readback ( const char * name, int x, int y, int w, int h, uint8_t * out_pixels )
854+ {
855+ GLint previous_read_framebuffer = 0 ;
856+ glGetIntegerv ( GL_READ_FRAMEBUFFER_BINDING, &previous_read_framebuffer );
857+ glPixelStorei ( GL_PACK_ALIGNMENT, 1 );
858+
859+ if ( std::strcmp ( name, " glyph_buffer" ) == 0 ) {
860+ glBindFramebuffer ( GL_READ_FRAMEBUFFER, fontcache_fbo[ 0 ] );
861+ glReadBuffer ( GL_COLOR_ATTACHMENT0 );
862+ glReadPixels ( x, y, w, h, GL_RED, GL_UNSIGNED_BYTE, out_pixels );
863+ } else if ( std::strcmp ( name, " atlas" ) == 0 ) {
864+ glBindFramebuffer ( GL_READ_FRAMEBUFFER, fontcache_fbo[ 1 ] );
865+ glReadBuffer ( GL_COLOR_ATTACHMENT0 );
866+ glReadPixels ( x, y, w, h, GL_RED, GL_UNSIGNED_BYTE, out_pixels );
867+ } else if ( std::strcmp ( name, " target" ) == 0 ) {
868+ glBindFramebuffer ( GL_READ_FRAMEBUFFER, 0 );
869+ glReadBuffer ( GL_BACK );
870+ glReadPixels ( x, y, w, h, GL_RED, GL_UNSIGNED_BYTE, out_pixels );
871+ } else {
872+ glBindFramebuffer ( GL_READ_FRAMEBUFFER, previous_read_framebuffer );
873+ return false ;
874+ }
875+
876+ glBindFramebuffer ( GL_READ_FRAMEBUFFER, previous_read_framebuffer );
877+ check_error ( __LINE__ );
878+ return true ;
879+ }
880+
881+ static void backend_test_execute ()
882+ {
883+ clear_framebuffer_colour ( 0 );
884+ fontcache_drawcmd ();
885+ glFinish ();
886+ }
887+
888+ static int run_backend_test_mode ()
889+ {
890+ ve_fontcache_init ( &cache, false );
891+ #ifdef VE_FONTCACHE_FREETYPE_RASTERISATION
892+ cache.use_freetype = false ;
893+ #endif // VE_FONTCACHE_FREETYPE_RASTERISATION
894+ ve_fontcache_configure_snap ( &cache, window_size.width , window_size.height );
895+
896+ std::vector< uint8_t > primary_buffer;
897+ std::vector< uint8_t > secondary_buffer;
898+ std::vector< uint8_t > small_buffer;
899+ std::vector< uint8_t > latin_buffer;
900+ std::vector< uint8_t > cjk_buffer;
901+ std::vector< uint8_t > huge_buffer;
902+ std::vector< std::vector< uint8_t > > reload_buffers;
903+
904+ ve_font_id primary_font = load_demo_font ( &cache, " fonts/NotoSansJP-Light.otf" , primary_buffer, 19 .0f );
905+ ve_font_id secondary_font = load_demo_font ( &cache, " fonts/OpenSans-Regular.ttf" , secondary_buffer, 48 .0f );
906+ ve_font_id small_test_font = load_demo_font ( &cache, " fonts/NotoSansJP-Light.otf" , small_buffer, 10 .0f );
907+ ve_font_id latin_test_font = load_demo_font ( &cache, " fonts/OpenSans-Regular.ttf" , latin_buffer, 42 .0f );
908+ ve_font_id cjk_test_font = load_demo_font ( &cache, " fonts/NotoSerifSC-Regular.otf" , cjk_buffer, 54 .0f );
909+ ve_font_id huge_test_font = load_demo_font ( &cache, " fonts/NotoSansJP-Light.otf" , huge_buffer, 200 .0f );
910+
911+ const bool fonts_ready =
912+ primary_font >= 0
913+ && secondary_font >= 0
914+ && small_test_font >= 0
915+ && latin_test_font >= 0
916+ && cjk_test_font >= 0
917+ && huge_test_font >= 0 ;
918+ if ( !fonts_ready ) {
919+ printf ( " VEFontCache backend tests failed to load one or more demo fonts.\n " );
920+ ve_fontcache_shutdown ( &cache );
921+ return 1 ;
922+ }
923+
924+ clear_backend_test_surfaces ();
925+
926+ ve_fontcache_backend_test_options options;
927+ options.cache = &cache;
928+ options.font = primary_font;
929+ options.secondary_font = secondary_font;
930+ options.small_font = small_test_font;
931+ options.latin_font = latin_test_font;
932+ options.cjk_font = cjk_test_font;
933+ options.huge_font = huge_test_font;
934+ options.execute = backend_test_execute;
935+ options.readback = backend_test_readback;
936+ options.reload_font = [ &reload_buffers ]() -> ve_font_id {
937+ reload_buffers.emplace_back ();
938+ return load_demo_font ( &cache, " fonts/NotoSansJP-Light.otf" , reload_buffers.back (), 19 .0f );
939+ };
940+
941+ ve_fontcache_backend_test_result result = ve_fontcache_backend_test_run ( options );
942+ printf (
943+ " VEFontCache backend tests: %d passed, %d failed, %d skipped\n " ,
944+ result.passed ,
945+ result.failed ,
946+ result.skipped );
947+ for ( const std::string& failure : result.failures ) {
948+ printf ( " FAIL: %s\n " , failure.c_str () );
949+ }
950+ for ( const std::string& skipped : result.skipped_tests ) {
951+ printf ( " SKIP: %s\n " , skipped.c_str () );
952+ }
953+
954+ ve_fontcache_shutdown ( &cache );
955+ return result.failed == 0 ? 0 : 1 ;
956+ }
957+
958+ int main ( int argc, char ** argv )
781959{
782960 TinyWindow::windowSetting_t cfg;
783961 cfg.name = " VEFontCache" ; cfg.versionMajor = 3 ; cfg.versionMinor = 3 ; cfg.enableSRGB = false ;
@@ -815,6 +993,13 @@ int main()
815993 }
816994#endif // VE_FONTCACHE_DEBUGPRINT
817995
996+ if ( has_flag ( argc, argv, " --test" ) ) {
997+ int exit_code = run_backend_test_mode ();
998+ manager->ShutDown ();
999+ window.reset ( nullptr );
1000+ return exit_code;
1001+ }
1002+
8181003 init_demo ();
8191004 while ( !window->shouldClose ) {
8201005 manager->PollForEvents ();
0 commit comments