#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 #include "../primitives/allegro5/allegro_primitives.h" 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! Using defaults\n"); cache_text_str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 "; } // 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 = abs(size); sft.yScale = abs(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 #define wmax (int32_t)1024 int32_t woff = 1, hoff = 1, hmax = 128; // signed to calculate difference at texture edges gtex = al_create_bitmap(wmax, hmax); al_set_target_bitmap(gtex); al_clear_to_color(al_map_rgba(0, 0, 0, 0)); // todo invert alpha at final pass unsigned int i = 0; const unsigned int len = strlen(cache_text_str); unsigned int lh = 0; while (i < len) { SFT_Glyph glyph; SFT_GMetrics geom; if (0 > sft_lookup(&sft, cache_text_str[i], &glyph)) goto glyph_measure_fail; if (0 > sft_gmetrics(&sft, glyph, &geom)) goto glyph_measure_fail; if (lh < geom.minHeight) lh = geom.minHeight; i++; continue; glyph_measure_fail: ALLEGRO_ERROR("Failure generating glyph %c in %s!\n", cache_text_str[i], filename); FAIL; } ALLEGRO_INFO("scanned all glyphs for font height %u", lh); i = 0; while (i < len) { 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; if (0 > sft_gmetrics(&sft, glyph, &geom)) goto glyph_lookup_fail; if ((wmax - woff) < geom.minWidth + 1) { hoff += lh + 1; woff = 1; } if ((hmax - hoff) < (geom.minHeight + abs(geom.yOffset)) + 1) { ALLEGRO_WARN("ran out of room! growing glyph texture\n"); gtex = grow_bitmap(gtex, hmax); al_set_target_bitmap(gtex); hmax *= 2; } img.width = geom.minWidth; img.height = geom.minHeight; img.pixels = malloc(img.width * img.height); if (!img.pixels) goto glyph_lookup_oom; if (0 > sft_render(&sft, glyph, img)) goto glyph_lookup_fail; ALLEGRO_INFO("got raster\n"); //al_draw_filled_rectangle(woff, hoff, woff + geom.minWidth, hoff + geom.minHeight, al_map_rgba(0, 0, 0, 255)); // fixme block = al_lock_bitmap_region(gtex, woff, hoff + (geom.yOffset + lh), geom.minWidth, geom.minHeight, ALLEGRO_PIXEL_FORMAT_SINGLE_CHANNEL_8, ALLEGRO_LOCK_WRITEONLY); assert(block->pixel_size == 1); ALLEGRO_INFO("got vram lock\n"); int row = 0; // should not be signed!!!!! C programmers are fucking assholes! while (row < img.height) { memcpy( (uint8_t*)block->data + block->pitch * row, (uint8_t*)img.pixels + img.width * row, img.width ); row++; } free(img.pixels); al_unlock_bitmap(gtex); ALLEGRO_INFO("blit raster to vram\n"); woff += geom.minWidth + 1; i++; continue; glyph_lookup_oom: ALLEGRO_ERROR("No buffer to generate glyph %c in %s\n", cache_text_str[i], filename); FAIL; 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); }