#include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hb-subset.h" enum operation_t { subset_codepoints, subset_glyphs }; #define SUBSET_FONT_BASE_PATH "test/subset/data/fonts/" struct test_input_t { const char *font_path; const unsigned max_subset_size; } default_tests[] = { {SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf", 4000}, {SUBSET_FONT_BASE_PATH "Amiri-Regular.ttf", 4000}, {SUBSET_FONT_BASE_PATH "NotoNastaliqUrdu-Regular.ttf", 1000}, {SUBSET_FONT_BASE_PATH "NotoSansDevanagari-Regular.ttf", 1000}, {SUBSET_FONT_BASE_PATH "Mplus1p-Regular.ttf", 10000}, {SUBSET_FONT_BASE_PATH "SourceHanSans-Regular_subset.otf", 10000}, {SUBSET_FONT_BASE_PATH "SourceSansPro-Regular.otf", 2000}, }; static test_input_t *tests = default_tests; static unsigned num_tests = sizeof (default_tests) / sizeof (default_tests[0]); // https://en.cppreference.com/w/cpp/thread/condition_variable/wait static std::condition_variable cv; static std::mutex cv_m; static bool ready = false; static unsigned num_repetitions = 1; static unsigned num_threads = 3; static void AddCodepoints(const hb_set_t* codepoints_in_font, unsigned subset_size, hb_subset_input_t* input) { auto *unicodes = hb_subset_input_unicode_set (input); hb_codepoint_t cp = HB_SET_VALUE_INVALID; for (unsigned i = 0; i < subset_size; i++) { if (!hb_set_next (codepoints_in_font, &cp)) return; hb_set_add (unicodes, cp); } } static void AddGlyphs(unsigned num_glyphs_in_font, unsigned subset_size, hb_subset_input_t* input) { auto *glyphs = hb_subset_input_glyph_set (input); for (unsigned i = 0; i < subset_size && i < num_glyphs_in_font; i++) { hb_set_add (glyphs, i); } } static void subset (operation_t operation, const test_input_t &test_input, hb_face_t *face) { // Wait till all threads are ready. { std::unique_lock lk (cv_m); cv.wait(lk, [] {return ready;}); } unsigned subset_size = test_input.max_subset_size; hb_subset_input_t* input = hb_subset_input_create_or_fail (); assert (input); switch (operation) { case subset_codepoints: { hb_set_t* all_codepoints = hb_set_create (); hb_face_collect_unicodes (face, all_codepoints); AddCodepoints(all_codepoints, subset_size, input); hb_set_destroy (all_codepoints); } break; case subset_glyphs: { unsigned num_glyphs = hb_face_get_glyph_count (face); AddGlyphs(num_glyphs, subset_size, input); } break; } for (unsigned i = 0; i < num_repetitions; i++) { hb_face_t* subset = hb_subset_or_fail (face, input); assert (subset); hb_face_destroy (subset); } hb_subset_input_destroy (input); } static void test_operation (operation_t operation, const char *operation_name, const test_input_t &test_input) { char name[1024] = "subset"; const char *p; strcat (name, "/"); p = strrchr (test_input.font_path, '/'); strcat (name, p ? p + 1 : test_input.font_path); strcat (name, "/"); strcat (name, operation_name); printf ("Testing %s\n", name); hb_face_t *face; { hb_blob_t *blob = hb_blob_create_from_file_or_fail (test_input.font_path); assert (blob); face = hb_face_create (blob, 0); hb_blob_destroy (blob); } std::vector threads; for (unsigned i = 0; i < num_threads; i++) threads.push_back (std::thread (subset, operation, test_input, face)); { std::unique_lock lk (cv_m); ready = true; } cv.notify_all(); for (unsigned i = 0; i < num_threads; i++) threads[i].join (); hb_face_destroy (face); } int main(int argc, char** argv) { if (argc > 1) num_threads = atoi (argv[1]); if (argc > 2) num_repetitions = atoi (argv[2]); if (argc > 4) { num_tests = argc - 3; tests = (test_input_t *) calloc (num_tests, sizeof (test_input_t)); for (unsigned i = 0; i < num_tests; i++) { tests[i].font_path = argv[3 + i]; } } printf ("Num threads %u; num repetitions %u\n", num_threads, num_repetitions); for (unsigned i = 0; i < num_tests; i++) { auto& test_input = tests[i]; test_operation (subset_codepoints, "codepoints", test_input); test_operation (subset_glyphs, "glyphs", test_input); } if (tests != default_tests) free (tests); }