From e91f5384b4dd66e95a5091c3f83e761cb1b1edbf Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 4 Oct 2015 03:30:20 -0700 Subject: [PATCH] text-freetype2: Cache data to reduce load time This caches the font list data to a file to minimize load times. Font data will be refreshed when any font files are added/removed, based upon a checksum of the font file names and dates (if available). --- plugins/text-freetype2/find-font-cocoa.m | 57 ++++++ plugins/text-freetype2/find-font-unix.c | 5 + plugins/text-freetype2/find-font-windows.c | 43 +++++ plugins/text-freetype2/find-font.c | 202 ++++++++++++++++++++- plugins/text-freetype2/find-font.h | 21 ++- plugins/text-freetype2/text-freetype2.c | 9 +- 6 files changed, 321 insertions(+), 16 deletions(-) diff --git a/plugins/text-freetype2/find-font-cocoa.m b/plugins/text-freetype2/find-font-cocoa.m index dba3c6151..4e4f55b8a 100644 --- a/plugins/text-freetype2/find-font-cocoa.m +++ b/plugins/text-freetype2/find-font-cocoa.m @@ -1,9 +1,12 @@ #include +#include #include "find-font.h" #include "text-freetype2.h" #import +extern void save_font_list(void); + static inline void add_path_font(const char *path) { FT_Face face; @@ -53,5 +56,59 @@ void load_os_font_list(void) if (folder_exists && is_dir) add_path_fonts(file_manager, font_path); } + + save_font_list(); } } + +static uint32_t add_font_checksum(uint32_t checksum, const char *path) +{ + if (path && *path) + checksum = calc_crc32(checksum, path, strlen(path)); + return checksum; +} + +static uint32_t add_font_checksum_path(uint32_t checksum, + NSFileManager *file_manager, NSString *path) +{ + NSArray *files = NULL; + + files = [file_manager contentsOfDirectoryAtPath:path error:nil]; + + for (NSString *file in files) { + NSString *full_path = [path stringByAppendingPathComponent:file]; + + checksum = add_font_checksum(checksum, + full_path.fileSystemRepresentation); + } + + return checksum; +} + +uint32_t get_font_checksum(void) +{ + uint32_t checksum = 0; + + @autoreleasepool { + BOOL is_dir; + NSArray *paths = NSSearchPathForDirectoriesInDomains( + NSLibraryDirectory, NSAllDomainsMask, true); + + for (NSString *path in paths) { + NSFileManager *file_manager = + [NSFileManager defaultManager]; + NSString *font_path = + [path stringByAppendingPathComponent:@"Fonts"]; + + bool folder_exists = [file_manager + fileExistsAtPath:font_path + isDirectory:&is_dir]; + + if (folder_exists && is_dir) + checksum = add_font_checksum_path(checksum, + file_manager, font_path); + } + } + + return checksum; +} diff --git a/plugins/text-freetype2/find-font-unix.c b/plugins/text-freetype2/find-font-unix.c index 3128065a9..05d83bcb6 100644 --- a/plugins/text-freetype2/find-font-unix.c +++ b/plugins/text-freetype2/find-font-unix.c @@ -26,6 +26,11 @@ void free_os_font_list(void) { } +bool load_cached_os_font_list(void) +{ + return true; +} + void load_os_font_list(void) { } diff --git a/plugins/text-freetype2/find-font-windows.c b/plugins/text-freetype2/find-font-windows.c index 119a53633..542a77d1d 100644 --- a/plugins/text-freetype2/find-font-windows.c +++ b/plugins/text-freetype2/find-font-windows.c @@ -1,5 +1,6 @@ #include #include +#include #include "find-font.h" #include "text-freetype2.h" @@ -9,6 +10,7 @@ #include extern DARRAY(struct font_path_info) font_list; +extern void save_font_list(void); struct mac_font_mapping { unsigned short encoding_id; @@ -191,6 +193,45 @@ char *sfnt_name_to_utf8(FT_SfntName *sfnt_name) return utf8_str; } +uint32_t get_font_checksum(void) +{ + uint32_t checksum = 0; + struct dstr path = {0}; + HANDLE handle; + WIN32_FIND_DATAA wfd; + + dstr_reserve(&path, MAX_PATH); + + HRESULT res = SHGetFolderPathA(NULL, CSIDL_FONTS, NULL, + SHGFP_TYPE_CURRENT, path.array); + if (res != S_OK) { + blog(LOG_WARNING, "Error finding windows font folder"); + return 0; + } + + path.len = strlen(path.array); + dstr_cat(&path, "\\*.*"); + + handle = FindFirstFileA(path.array, &wfd); + if (handle == INVALID_HANDLE_VALUE) + goto free_string; + + dstr_resize(&path, path.len - 4); + + do { + checksum = calc_crc32(checksum, &wfd.ftLastWriteTime, + sizeof(FILETIME)); + checksum = calc_crc32(checksum, wfd.cFileName, + strlen(wfd.cFileName)); + } while (FindNextFileA(handle, &wfd)); + + FindClose(handle); + +free_string: + dstr_free(&path); + return checksum; +} + void load_os_font_list(void) { struct dstr path = {0}; @@ -244,6 +285,8 @@ void load_os_font_list(void) FindClose(handle); + save_font_list(); + free_string: dstr_free(&path); } diff --git a/plugins/text-freetype2/find-font.c b/plugins/text-freetype2/find-font.c index 969fbfa59..888cb1b83 100644 --- a/plugins/text-freetype2/find-font.c +++ b/plugins/text-freetype2/find-font.c @@ -1,9 +1,201 @@ +#include #include -#include +#include +#include #include "find-font.h" DARRAY(struct font_path_info) font_list; +static inline bool read_data(struct serializer *s, void *data, size_t size) +{ + return s_read(s, data, size) == size; +} + +static inline bool write_data(struct serializer *s, const void *data, + size_t size) +{ + return s_write(s, data, size) == size; +} + +#define read_var(s, data) read_data(s, &data, sizeof(data)) +#define write_var(s, data) write_data(s, &data, sizeof(data)) + +static bool read_str(struct serializer *s, char **p_str) +{ + size_t size; + char *str; + + if (!read_var(s, size)) + return false; + + str = bmalloc(size + 1); + if (size && !read_data(s, str, size)) { + bfree(str); + return false; + } + + str[size] = 0; + *p_str = str; + return true; +} + +static bool write_str(struct serializer *s, const char *str) +{ + size_t size = str ? strlen(str) : 0; + + if (!write_var(s, size)) + return false; + if (size && !write_data(s, str, size)) + return false; + return true; +} + +static bool load_cached_font_list(struct serializer *s) +{ + bool success = true; + int count; + + success = read_var(s, count); + if (!success) return false; + + da_init(font_list); + da_resize(font_list, count); + +#define do_read(var) \ + success = read_var(s, var); \ + if (!success) break + + for (int i = 0; i < count; i++) { + struct font_path_info *info = &font_list.array[i]; + + success = read_str(s, &info->face_and_style); + if (!success) break; + + do_read(info->full_len); + do_read(info->face_len); + do_read(info->is_bitmap); + do_read(info->num_sizes); + + info->sizes = bmalloc(sizeof(int) * info->num_sizes); + success = read_data(s, info->sizes, + sizeof(int) * info->num_sizes); + if (!success) break; + + do_read(info->bold); + + success = read_str(s, &info->path); + if (!success) break; + + do_read(info->italic); + do_read(info->index); + } + +#undef do_read + + if (!success) { + free_os_font_list(); + return false; + } + + return true; +} + +extern uint32_t get_font_checksum(); +static const uint32_t font_cache_ver = 1; + +bool load_cached_os_font_list(void) +{ + char *file_name = obs_module_config_path("font_data.bin"); + uint32_t old_checksum; + uint32_t new_checksum; + struct serializer s; + uint32_t ver; + bool success; + + success = file_input_serializer_init(&s, file_name); + bfree(file_name); + + if (!success) + return false; + + success = read_data(&s, &ver, sizeof(ver)); + + if (!success || ver != font_cache_ver) { + success = false; + goto finish; + } + + success = s_read(&s, &old_checksum, sizeof(old_checksum)); + new_checksum = get_font_checksum(); + + if (!success || old_checksum != new_checksum) { + success = false; + goto finish; + } + + success = load_cached_font_list(&s); + +finish: + file_input_serializer_free(&s); + return success; +} + +void save_font_list(void) +{ + char *file_name = obs_module_config_path("font_data.bin"); + uint32_t font_checksum = get_font_checksum(); + int font_count = (int)font_list.num; + struct serializer s; + bool success = false; + + if (font_checksum) + success = file_output_serializer_init_safe(&s, file_name, + "tmp"); + bfree(file_name); + + if (!success) + return; + + success = write_var(&s, font_cache_ver); + if (!success) return; + success = write_var(&s, font_checksum); + if (!success) return; + success = write_var(&s, font_count); + if (!success) return; + +#define do_write(var) \ + success = write_var(&s, var); \ + if (!success) break + + for (size_t i = 0; i < font_list.num; i++) { + struct font_path_info *info = &font_list.array[i]; + + success = write_str(&s, info->face_and_style); + if (!success) break; + + do_write(info->full_len); + do_write(info->face_len); + do_write(info->is_bitmap); + do_write(info->num_sizes); + + success = write_data(&s, info->sizes, + sizeof(int) * info->num_sizes); + if (!success) break; + + do_write(info->bold); + + success = write_str(&s, info->path); + if (!success) break; + + do_write(info->italic); + do_write(info->index); + } + +#undef do_write + + file_output_serializer_free(&s); +} + static void create_bitmap_sizes(struct font_path_info *info, FT_Face face) { DARRAY(int) sizes; @@ -23,7 +215,7 @@ static void create_bitmap_sizes(struct font_path_info *info, FT_Face face) } info->sizes = sizes.array; - info->num_sizes = face->num_fixed_sizes; + info->num_sizes = (uint32_t)face->num_fixed_sizes; } static void add_font_path(FT_Face face, @@ -57,8 +249,8 @@ static void add_font_path(FT_Face face, } info.face_and_style = face_and_style.array; - info.full_len = face_and_style.len; - info.face_len = strlen(family_in); + info.full_len = (uint32_t)face_and_style.len; + info.face_len = (uint32_t)strlen(family_in); info.is_bitmap = !!(face->face_flags & FT_FACE_FLAG_FIXED_SIZES); info.bold = !!(face->style_flags & FT_STYLE_FLAG_BOLD); @@ -182,7 +374,7 @@ const char *get_font_path(const char *family, uint16_t size, const char *style, if (info->is_bitmap) { int best_diff = 1000; - for (size_t j = 0; j < info->num_sizes; j++) { + for (uint32_t j = 0; j < info->num_sizes; j++) { int diff = abs(info->sizes[j] - size); if (diff < best_diff) best_diff = diff; diff --git a/plugins/text-freetype2/find-font.h b/plugins/text-freetype2/find-font.h index 1543175c2..198fb1534 100644 --- a/plugins/text-freetype2/find-font.h +++ b/plugins/text-freetype2/find-font.h @@ -9,19 +9,19 @@ #include struct font_path_info { - char *face_and_style; - size_t full_len; - size_t face_len; + char *face_and_style; + uint32_t full_len; + uint32_t face_len; - bool is_bitmap; - size_t num_sizes; - int *sizes; + bool is_bitmap; + uint32_t num_sizes; + int *sizes; - bool bold; - bool italic; + bool bold; + bool italic; - char *path; - FT_Long index; + char *path; + FT_Long index; }; static inline void font_path_info_free(struct font_path_info *info) @@ -34,6 +34,7 @@ static inline void font_path_info_free(struct font_path_info *info) extern void build_font_path_info(FT_Face face, FT_Long idx, const char *path); extern char *sfnt_name_to_utf8(FT_SfntName *sfnt_name); +extern bool load_cached_os_font_list(void); extern void load_os_font_list(void); extern void free_os_font_list(void); extern const char *get_font_path(const char *family, uint16_t size, diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index b26344b26..b4cedc11f 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -48,6 +48,12 @@ static struct obs_source_info freetype2_source_info = { bool obs_module_load() { + char *config_dir = obs_module_config_path(NULL); + if (config_dir) { + os_mkdirs(config_dir); + bfree(config_dir); + } + FT_Init_FreeType(&ft2_lib); if (ft2_lib == NULL) { @@ -55,7 +61,8 @@ bool obs_module_load() return false; } - load_os_font_list(); + if (!load_cached_os_font_list()) + load_os_font_list(); obs_register_source(&freetype2_source_info);