From ea76b6988ccafaa6a4d4ed90f2489d0e49e1f180 Mon Sep 17 00:00:00 2001 From: Benjamin Barenblat Date: Sat, 5 Sep 2015 09:32:30 -0400 Subject: Imported Upstream version 0.40.24 --- g_src/ttf_manager.cpp | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100755 g_src/ttf_manager.cpp (limited to 'g_src/ttf_manager.cpp') diff --git a/g_src/ttf_manager.cpp b/g_src/ttf_manager.cpp new file mode 100755 index 0000000..f873a40 --- /dev/null +++ b/g_src/ttf_manager.cpp @@ -0,0 +1,245 @@ +#include "ttf_manager.hpp" +#include "init.h" +#include + +using namespace std; + +ttf_managerst ttf_manager; + +bool ttf_managerst::init(int ceiling, int tile_width) { + // Reset to a known state, clear everything + if ((!TTF_WasInit()) && (TTF_Init() == -1)) { + MessageBox(NULL, TTF_GetError(), "TTF error", MB_OK); + return false; + } + if (font) TTF_CloseFont(font); + handles.clear(); + for (auto it = textures.cbegin(); it != textures.cend(); ++it) + SDL_FreeSurface(it->second); + textures.clear(); + this->tile_width = tile_width; + this->ceiling = ceiling; + // Try progressively smaller point sizes until we find one that fits + for (int sz=20; sz > 0; sz--) { + font = TTF_OpenFont("data/art/font.ttf", sz); + if (!font) continue; + if (TTF_FontHeight(font) <= ceiling) { +#ifdef DEBUG + cout << "Picked font at " << sz << " points for ceiling " << ceiling << endl; + // get the glyph metric for the letter 'M' in a loaded font + cout << "TTF_FontHeight " << TTF_FontHeight(font) << endl; + cout << "TTF_FontAscent " << TTF_FontAscent(font) << endl; + cout << "TTF_FontDescent " << TTF_FontDescent(font) << endl; + cout << "TTF_FontLineSkip " << TTF_FontLineSkip(font) << endl; +#endif + int minx,maxx,miny,maxy,advance; + if (TTF_GlyphMetrics(font, 'M', &minx, &maxx, &miny, &maxy, &advance) == -1) + puts(TTF_GetError()); + else { + em_width = maxx; +#ifdef DEBUG + printf("minx : %d\n",minx); + printf("maxx : %d\n",maxx); + printf("miny : %d\n",miny); + printf("maxy : %d\n",maxy); + printf("advance : %d\n",advance); +#endif + } + return true; + } + TTF_CloseFont(font); + } + // ..fine. + cout << "No font found!" << endl; + font = NULL; + return false; +} + +static void cp437_to_unicode(const string &text, vector &unicode) { + unicode.resize(text.length() + 1); + int i; + for (i=0; i < text.size(); i++) { + const int cp437 = (unsigned char)text[i]; + unicode[i] = charmap[cp437]; + } + unicode[i] = 0; +} + + +int ttf_managerst::size_text(const string &text) { + vector text_unicode; + cp437_to_unicode(text, text_unicode); + int width, height; + TTF_SizeUNICODE(font, &text_unicode[0], &width, &height); + return (width + tile_width - 1) / tile_width; +} + + +ttf_details ttf_managerst::get_handle(const list &text, justification just) { + // Check for an existing handle + handleid id = {text, just}; + auto it = handles.find(id); + if (it != handles.end()) return it->second; + // Right. Make a new one. + int handle = ++max_handle; + // Split out any tabs + list split_text; + for (auto it = text.cbegin(); it != text.cend(); ++it) { + int pos = 0; + int tabpos; + while ((tabpos = it->text.find("\t", pos)) != string::npos) { + ttf_id left; + left.fg = it->fg; left.bg = it->bg; left.bold = it->bold; + left.text = it->text.substr(pos, tabpos - pos); + split_text.push_back(left); + ttf_id tabber; + tabber.fg = tabber.bg = tabber.bold = 255; + split_text.push_back(tabber); + pos = tabpos + 1; + } + ttf_id right; + right.fg = it->fg; right.bg = it->bg; right.bold = it->bold; + right.text = it->text.substr(pos); + split_text.push_back(right); + } + // Find the total width of the text + vector text_unicode; + int ttf_width = 0, ttf_height = 0, text_width = 0; + for (auto it = split_text.cbegin(); it != split_text.cend(); ++it) { + if (it->fg == 255 && it->bg == 255 && it->bold == 255) { + // Tab stop + int tabstop = tab_width * em_width; + int tab_width = tabstop - ((ttf_width - 1) % tabstop) + 1; + ttf_width += tab_width; + text_width += 1; + } else { + cp437_to_unicode(it->text, text_unicode); + int slice_width, slice_height; + TTF_SizeUNICODE(font, &text_unicode[0], &slice_width, &slice_height); + ttf_width += slice_width; + text_width += it->text.size(); + } + } + ttf_height = ceiling; + // Compute geometry + double grid_width = double(ttf_width) / tile_width; + double offset = just == justify_right ? text_width - grid_width : + just == justify_center ? (text_width - grid_width) / 2 : + 0; + if (just == justify_center && text_width % 2) + offset += 0.5; // Arbitrary fixup for approximate grid centering + double fraction, integral; + fraction = modf(offset, &integral); + // Outputs: + const int grid_offset = int(integral + 0.001); // Tiles to move to the right in addst + const int pixel_offset = int(fraction * tile_width); // Black columns to add to the left of the image + // const int full_grid_width = int(ceil(double(ttf_width) / double(tile_width) + fraction) + 0.1); // Total width of the image in grid units + const int full_grid_width = text_width; + const int pixel_width = full_grid_width * tile_width; // And pixels + assert(pixel_width >= ttf_width); + // Store for later + ttf_details ret; ret.handle = handle; ret.offset = grid_offset; ret.width = full_grid_width; + handles[id] = ret; + // We do the actual rendering in the render thread, later on. + todo.push_back(todum(handle, split_text, ttf_height, pixel_offset, pixel_width)); + return ret; +} + +SDL_Surface *ttf_managerst::get_texture(int handle) { + // Run any outstanding renders + if (!todo.empty()) { + vector text_unicode; + for (auto it = todo.cbegin(); it != todo.cend(); ++it) { + const int height = it->height; + SDL_Surface *textimg = SDL_CreateRGBSurface(SDL_SWSURFACE, it->pixel_width, height, 32, 0, 0, 0, 0); +// #ifdef DEBUG +// SDL_FillRect(textimg, NULL, SDL_MapRGBA(textimg->format, 255, 0, 0, 255)); +// #endif + // Render each of the text segments + int idx = 0; + int xpos = it->pixel_offset; + for (auto seg = it->text.cbegin(); seg != it->text.cend();) { + const ttf_id &text = *seg; + ++seg; + ++idx; + if (text.fg == 255 && text.bg == 255 && text.bold == 255) { + // Skip to tab stop + int tabstop = tab_width * em_width; + int tab_width = tabstop - ((xpos - 1) % tabstop) + 1; + xpos += tab_width; + continue; + } + if (text.text.size() <= 0) + continue; + cp437_to_unicode(text.text, text_unicode); + const int fg = (text.fg + text.bold * 8) % 16; + SDL_Color fgc = {Uint8(enabler.ccolor[fg][0]*255), + Uint8(enabler.ccolor[fg][1]*255), + Uint8(enabler.ccolor[fg][2]*255)}; + const int bg = text.bg % 16; + Uint32 bgc = SDL_MapRGB(textimg->format, + Uint8(enabler.ccolor[bg][0]*255), + Uint8(enabler.ccolor[bg][1]*255), + Uint8(enabler.ccolor[bg][2]*255)); +#ifdef DEBUG + // SDL_Color white = {255,255,255}; + // Uint32 red = SDL_MapRGB(textimg->format, 255,0,0); + // fgc = white; + // bgc = red; +#endif + if (idx == 0) { + // Fill in the left side + SDL_Rect left = {0, 0, Sint16(xpos), Sint16(height)}; + SDL_FillRect(textimg, &left, bgc); + } else if (seg == it->text.cend()) { + // Fill in the right side + SDL_Rect right = {Sint16(xpos), 0, Sint16(it->pixel_width), Sint16(height)}; + SDL_FillRect(textimg, &right, bgc); + } + // Render the TTF segment + SDL_Surface *textimg_seg = TTF_RenderUNICODE_Blended(font, &text_unicode[0], fgc); + // Fill the background color of this part of the textimg + SDL_Rect dest = {Sint16(xpos), 0, Sint16(textimg_seg->w), Sint16(height)}; + SDL_FillRect(textimg, &dest, + SDL_MapRGB(textimg->format, + // Uint8(255), + // Uint8(255), + // Uint8(255))); + Uint8(enabler.ccolor[bg][0]*255), + Uint8(enabler.ccolor[bg][1]*255), + Uint8(enabler.ccolor[bg][2]*255))); + // And copy the TTF segment over. + SDL_Rect dest2 = {Sint16(xpos), 0, 0, 0}; + SDL_BlitSurface(textimg_seg, NULL, textimg, &dest2); + // Ready for next segment. + xpos += textimg_seg->w; + SDL_FreeSurface(textimg_seg); + } + // ..and make the whole thing display format. Phew! + SDL_Surface *textimg_2 = SDL_DisplayFormat(textimg); +#ifdef DEBUG + // cout << "Rendering \"" << text.text << "\" at height " << box2->h << endl; + // cout << " width " << textimg->w << " in box of " << box->w << endl; +#endif + SDL_FreeSurface(textimg); + // Store it for later. + textures[it->handle] = textimg_2; + } + todo.clear(); + } + // Find the li'l texture + SDL_Surface *tex = textures[handle]; + if (!tex) { + cout << "Missing/broken TTF handle: " << handle << endl; + } + return tex; +} + +void ttf_managerst::gc() { + // Just delete everything, for now. + for (auto it = textures.begin(); it != textures.end(); ++it) + SDL_FreeSurface(it->second); + textures.clear(); + handles.clear(); + todo.clear(); +} -- cgit v1.2.3