#define ALLEGRO_INTERNAL_UNSTABLE #include "allegro5/allegro.h" #include "allegro5/internal/aintern.h" #include "allegro5/internal/aintern_vector.h" #include "allegro5/allegro_schrift.h" #include "allegro5/internal/aintern_font.h" #include "allegro5/internal/aintern_dtor.h" #include "allegro5/internal/aintern_system.h" #include "allegro5/internal/aintern_exitfunc.h" #include "schrift.h" #include ALLEGRO_DEBUG_CHANNEL("font") #define CANI_ALLEGRO_SCHRIFT_VER 1 /* globals */ static bool ttf_inited; // Destroys the source bitmap and replaces it with a version // expanded downward by add no. of pixels. ALLEGRO_BITMAP *grow_bitmap(ALLEGRO_BITMAP *bmp, uint16_t add) { const uint32_t h = al_get_bitmap_height(bmp) + add; ALLEGRO_BITMAP *tgt = al_get_target_bitmap(); ALLEGRO_BITMAP *grown = al_create_bitmap(al_get_bitmap_width(bmp), h); al_set_target_bitmap(grown); al_draw_bitmap(bmp, 0, 0, 0); al_set_target_bitmap(tgt); al_destroy_bitmap(bmp); return grown; } ALLEGRO_FONT *al_load_ttf_font_f(ALLEGRO_FILE *file, char const *filename, int size, int flags) { #define CLEANUP free(filebuf); if (gtex) al_destroy_bitmap(gtex); al_set_target_bitmap(tgt) #define FAIL CLEANUP; return NULL const int64_t filesize = al_fsize(file); // this should not be signed! if (filesize < 1) { ALLEGRO_ERROR("Can't load font %s: file is empty or unknown size.\n", filename); return NULL; } void *filebuf = malloc(filesize); SFT sft = { .flags = SFT_DOWNWARD_Y }; ALLEGRO_FONT *f = NULL; ALLEGRO_BITMAP *gtex = NULL; int result = 0; ALLEGRO_BITMAP *tgt = al_get_target_bitmap(); ALLEGRO_CONFIG* system_cfg = al_get_system_config(); const char* cache_text_str = al_get_config_value(system_cfg, "ttf", "cache_text"); if (!cache_text_str) { ALLEGRO_ERROR("No characters defined in cache_text, no glyphs to bake!\n", filename); FAIL; } // read font file #define chunksize 1024 size_t offset = 0; while (!al_feof(file)) { if (al_ferror(file)) { ALLEGRO_ERROR("Can't load font %s: error reading file.\n", filename); FAIL; } offset += al_fread(file, (uint8_t*)filebuf + offset, chunksize); } sft.xScale = size; sft.yScale = size; sft.font = sft_loadmem(filebuf, filesize); if (!sft.font) { ALLEGRO_ERROR("Can't load font %s: schrift error, is it a valid format?\n", filename); FAIL; } // convert glyphs into textures todo gtex = al_create_bitmap(2048, 2048); uint16_t woff = 1, hoff = 0; al_set_target_bitmap(gtex); al_clear_to_color(al_map_rgba(0, 0, 0, 255)); unsigned int i = 0; while (cache_text_str[i] != '\0') { ALLEGRO_INFO("generating glyph for %c\n", cache_text_str[i]); SFT_Glyph glyph; SFT_GMetrics geom; SFT_Image img; ALLEGRO_LOCKED_REGION *block = NULL; if (0 > sft_lookup(&sft, cache_text_str[i], &glyph)) goto glyph_lookup_fail; ALLEGRO_INFO("got glyph\n"); if (0 > sft_gmetrics(&sft, glyph, &geom)) goto glyph_lookup_fail; ALLEGRO_INFO("got geom\n"); block = al_lock_bitmap_region(gtex, woff, hoff, geom.minWidth, geom.minHeight, ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8, ALLEGRO_LOCK_WRITEONLY); ALLEGRO_INFO("got lock\n"); img.width = geom.minWidth; img.height = geom.minHeight; img.pixels = block->data; if (0 > sft_render(&sft, glyph, img)) goto glyph_lookup_fail; ALLEGRO_INFO("got raster\n"); al_unlock_bitmap(gtex); ALLEGRO_INFO("unlock\n"); i++; continue; glyph_lookup_fail: ALLEGRO_ERROR("Failure generating glyph %c in %s!\n", cache_text_str[i], filename); FAIL; } // debug al_save_bitmap("schrift_test.png", gtex); // convert texture to bitmap font const unsigned int ranges_size = i*2; int ranges[ranges_size]; i = 0; while (i < ranges_size) { ranges[i] = (unsigned char)cache_text_str[i]; ranges[i+1] = (unsigned char)cache_text_str[i]; i++; i++; } f = al_grab_font_from_bitmap(gtex, ranges_size, ranges); f->dtor_item = _al_register_destructor(_al_dtor_list, "ttf_font", f, (void (*)(void *))al_destroy_font); ALLEGRO_DEBUG("Font %s loaded.\n", filename); CLEANUP; return f; } ALLEGRO_FONT *al_load_ttf_font(char const *filename, int size, int flags) { ALLEGRO_FILE *f; ALLEGRO_FONT *font; ASSERT(filename); f = al_fopen(filename, "rb"); if (!f) { ALLEGRO_ERROR("Unable to open file for reading: %s\n", filename); return NULL; } font = al_load_ttf_font_f(f, filename, size, flags); return font; } bool al_init_ttf_addon(void) { if (ttf_inited) { ALLEGRO_WARN("TTF addon already initialised.\n"); return true; } al_register_font_loader(".ttf", al_load_ttf_font); _al_add_exit_func(al_shutdown_ttf_addon, "al_shutdown_ttf_addon"); ttf_inited = true; return ttf_inited; } bool al_is_ttf_addon_initialized(void) { return ttf_inited; } void al_shutdown_ttf_addon(void) { if (!ttf_inited) { ALLEGRO_ERROR("TTF addon not initialised.\n"); return; } al_register_font_loader(".ttf", NULL); ttf_inited = false; } uint32_t al_get_allegro_ttf_version(void) { return CANI_ALLEGRO_SCHRIFT_VER; } ALLEGRO_FONT *al_load_ttf_font_stretch(char const *filename, int w, int h, int flags) { ALLEGRO_WARN("Unimplemented function\n"); al_load_ttf_font(filename, h, flags); } ALLEGRO_FONT *al_load_ttf_font_stretch_f(ALLEGRO_FILE *file, char const *filename, int w, int h, int flags) { ALLEGRO_WARN("Unimplemented function\n"); al_load_ttf_font_f(file, filename, h, flags); }