diff options
author | Ixtli <cg@325i.org> | 2010-03-14 20:40:54 +0900 |
---|---|---|
committer | Enne Walker <ennewalker@users.sourceforge.net> | 2010-04-24 10:19:45 -0400 |
commit | 149c76283904371235ea6d564037900edb8b07af (patch) | |
tree | 6bcde02f7acf2485f7e557bb4c64f74d3abefa9d /crawl-ref/source/tilefont.cc | |
parent | bab61331e9e1287ae14afe2644b68f4c49ff09d8 (diff) | |
download | crawl-ref-149c76283904371235ea6d564037900edb8b07af.tar.gz crawl-ref-149c76283904371235ea6d564037900edb8b07af.zip |
Created FontWrapper, and made FT flag in makefile.
Diffstat (limited to 'crawl-ref/source/tilefont.cc')
-rw-r--r-- | crawl-ref/source/tilefont.cc | 787 |
1 files changed, 8 insertions, 779 deletions
diff --git a/crawl-ref/source/tilefont.cc b/crawl-ref/source/tilefont.cc index 30f7d561bf..8d2b9d4793 100644 --- a/crawl-ref/source/tilefont.cc +++ b/crawl-ref/source/tilefont.cc @@ -1,788 +1,17 @@ -/* - * File: tilefont.cc - * Created by: ennewalker on Sat Apr 26 01:33:53 2008 UTC - */ - #include "AppHdr.h" #ifdef USE_TILE - -#include "tilebuf.h" #include "tilefont.h" -#include "defines.h" -#include "files.h" -#include "glwrapper.h" - -#include <ft2build.h> -#include FT_FREETYPE_H - -const VColour term_colours[MAX_TERM_COLOUR] = -{ - VColour( 0, 0, 0), // BLACK - VColour( 0, 82, 255), // BLUE - VColour(100, 185, 70), // GREEN - VColour( 0, 180, 180), // CYAN - VColour(255, 48, 0), // RED - VColour(238, 92, 238), // MAGENTA - VColour(165, 91, 0), // BROWN - VColour(162, 162, 162), // LIGHTGREY - VColour( 82, 82, 82), // DARKGREY - VColour( 82, 102, 255), // LIGHTBLUE - VColour( 82, 255, 82), // LIGHTGREEN - VColour( 82, 255, 255), // LIGHTCYAN - VColour(255, 82, 82), // LIGHTRED - VColour(255, 82, 255), // LIGHTMAGENTA - VColour(255, 255, 82), // YELLOW - VColour(255, 255, 255) // WHITE -}; - -FTFont::FTFont() : - m_glyphs(NULL), - m_max_advance(0, 0), - m_min_offset(0) -{ -} - -FTFont::~FTFont() -{ - delete[] m_glyphs; -} - -bool FTFont::load_font(const char *font_name, unsigned int font_size, bool outl) -{ - FT_Library library; - FT_Face face; - FT_Error error; - - error = FT_Init_FreeType(&library); - if (error) - { - fprintf(stderr, "Failed to initialise freetype library.\n"); - return false; - } - - // TODO enne - need to find a cross-platform way to also - // attempt to locate system fonts by name... - std::string font_path = datafile_path(font_name, false, true); - if (font_path.c_str()[0] == 0) - { - fprintf(stderr, "Could not find font '%s'\n", font_name); - return false; - } - - error = FT_New_Face(library, font_path.c_str(), 0, &face); - if (error == FT_Err_Unknown_File_Format) - { - fprintf(stderr, "Unknown font format for file '%s'\n", - font_path.c_str()); - return false; - } - else if (error) - { - fprintf(stderr, "Invalid font from file '%s'\n", font_path.c_str()); - } - - error = FT_Set_Pixel_Sizes(face, font_size, font_size); - ASSERT(!error); - - // Get maximum advance - m_max_advance = coord_def(0,0); - int ascender = face->ascender >> 6; - int min_y = 100000; - int max_y = 0; - int max_width = 0; - m_min_offset = 0; - m_glyphs = new GlyphInfo[256]; - for (unsigned int c = 0; c < 256; c++) - { - m_glyphs[c].offset = 0; - m_glyphs[c].advance = 0; - m_glyphs[c].renderable = false; - - FT_Int glyph_index = FT_Get_Char_Index(face, c); - if (!glyph_index) - continue; - - error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER); - ASSERT(!error); - - FT_Bitmap *bmp = &face->glyph->bitmap; - - int advance = face->glyph->advance.x >> 6; - - m_max_advance.x = std::max(m_max_advance.x, advance); - - int bmp_width = bmp->width; - int bmp_top = ascender - face->glyph->bitmap_top; - int bmp_bottom = ascender + bmp->rows - face->glyph->bitmap_top; - if (outl) - { - bmp_width += 2; - bmp_top -= 1; - bmp_bottom += 1; - } - - max_width = std::max(max_width, bmp_width); - min_y = std::min(min_y, bmp_top); - max_y = std::max(max_y, bmp_bottom); - - m_glyphs[c].offset = face->glyph->bitmap_left; - m_glyphs[c].advance = advance; - m_glyphs[c].width = bmp_width; - - m_min_offset = std::min((char)m_min_offset, m_glyphs[c].offset); - } - - // The ascender and text height given by FreeType2 is ridiculously large - // (e.g. 37 pixels high for 14 pixel font). Use min and max bounding - // heights on the characters we care about to get better values for the - // text height and the ascender. - m_max_advance.y = max_y - min_y; - ascender -= min_y; - - int max_height = m_max_advance.y; - - // Grow character size to power of 2 - coord_def charsz(1,1); - while (charsz.x < max_width) - charsz.x *= 2; - while (charsz.y < max_height) - charsz.y *= 2; - - // Fill out texture to be (16*charsz.x) X (16*charsz.y) X (32-bit) - // Having to blow out 8-bit alpha values into full 32-bit textures is - // kind of frustrating, but not all OpenGL implementations support the - // "esoteric" ALPHA8 format and it's not like this texture is very large. - unsigned int width = 16 * charsz.x; - unsigned int height = 16 * charsz.y; - unsigned char *pixels = new unsigned char[4 * width * height]; - memset(pixels, 0, sizeof(unsigned char) * 4 * width * height); - - // Special case c = 0 for full block. - { - m_glyphs[0].renderable = false; - for (int x = 0; x < max_width; x++) - for (int y = 0; y < max_height; y++) - { - unsigned int idx = x + y * width; - idx *= 4; - pixels[idx] = 255; - pixels[idx + 1] = 255; - pixels[idx + 2] = 255; - pixels[idx + 3] = 255; - } - } - - for (unsigned int c = 1; c < 256; c++) - { - FT_Int glyph_index = FT_Get_Char_Index(face, c); - if (!glyph_index) - { - // If no mapping for this character, leave blank. - continue; - } - - error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER); - ASSERT(!error); - - FT_Bitmap *bmp = &face->glyph->bitmap; - ASSERT(bmp); - // Some glyphs (e.g. ' ') don't get a buffer. - if (!bmp->buffer) - continue; - - m_glyphs[c].renderable = true; - - int vert_offset = ascender - face->glyph->bitmap_top; - - ASSERT(bmp->pixel_mode == FT_PIXEL_MODE_GRAY); - ASSERT(bmp->num_grays == 256); - - // Horizontal offset stored in m_glyphs and handled when drawing - unsigned int offset_x = (c % 16) * charsz.x; - unsigned int offset_y = (c / 16) * charsz.y + vert_offset; - - if (outl) - { - const int charw = bmp->width; - for (int x = -1; x <= bmp->width; x++) - for (int y = -1; y <= bmp->rows; y++) - { - bool x_valid = x >= 0 && x < bmp->width; - bool y_valid = y >= 0 && y < bmp->rows; - bool valid = x_valid && y_valid; - unsigned char orig = valid ? bmp->buffer[x + charw * y] : 0; - - unsigned char edge = 0; - if (y_valid && x > 0) - edge = std::max(bmp->buffer[(x-1) + charw * y], edge); - if (x_valid && y > 0) - edge = std::max(bmp->buffer[x + charw * (y-1)], edge); - if (y_valid && x < bmp->width - 1) - edge = std::max(bmp->buffer[(x+1) + charw * y], edge); - if (x_valid && y < bmp->rows - 1) - edge = std::max(bmp->buffer[x + charw * (y+1)], edge); - - unsigned int idx = offset_x+x+1 + (offset_y+y+1) * width; - idx *= 4; - - pixels[idx] = orig; - pixels[idx + 1] = orig; - pixels[idx + 2] = orig; - pixels[idx + 3] = std::min(orig + edge, 255); - } - } - else - { - for (int x = 0; x < bmp->width; x++) - for (int y = 0; y < bmp->rows; y++) - { - unsigned int idx = offset_x + x + (offset_y + y) * width; - idx *= 4; - unsigned char alpha = bmp->buffer[x + bmp->width * y]; - pixels[idx] = 255; - pixels[idx + 1] = 255; - pixels[idx + 2] = 255; - pixels[idx + 3] = alpha; - } - } - } - - bool success = m_tex.load_texture(pixels, width, height, - MIPMAP_NONE); - - delete[] pixels; - - return success; -} - -struct FontVertLayout -{ - float pos_x; - float pos_y; - float tex_x; - float tex_y; - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -}; - -void FTFont::render_textblock(unsigned int x_pos, unsigned int y_pos, - unsigned char *chars, unsigned char *colours, - unsigned int width, unsigned int height, - bool drop_shadow) -{ - if (!chars || !colours || !width || !height || !m_glyphs) - return; - - coord_def adv(std::max(-m_min_offset, 0), 0); - unsigned int i = 0; - - std::vector<FontVertLayout> verts; - // TODO enne - make this better - // This is bad for the CRT. Maybe we should just reserve some fixed limit? - // Maybe we should just cache this in FTFont? - verts.reserve(4 * width * height); - - float texcoord_dy = (float)m_max_advance.y / (float)m_tex.height(); - - for (unsigned int y = 0; y < height; y++) - { - for (unsigned int x = 0; x < width; x++) - { - unsigned char c = chars[i]; - unsigned char col_bg = colours[i] >> 4; - unsigned char col_fg = colours[i] & 0xF; - - if (col_bg != 0) - { - FontVertLayout v; - v.tex_x = v.tex_y = 0; - v.r = term_colours[col_bg].r; - v.g = term_colours[col_bg].g; - v.b = term_colours[col_bg].b; - v.a = 255; - - v.pos_x = adv.x; - v.pos_y = adv.y; - verts.push_back(v); - - v.pos_x = adv.x; - v.pos_y = adv.y + m_max_advance.y; - verts.push_back(v); - - v.pos_x = adv.x + m_max_advance.x; - v.pos_y = adv.y + m_max_advance.y; - verts.push_back(v); - - v.pos_x = adv.x + m_max_advance.x; - v.pos_y = adv.y; - verts.push_back(v); - } - - adv.x += m_glyphs[c].offset; - - if (m_glyphs[c].renderable) - { - int this_width = m_glyphs[c].width; - - float tex_x = (float)(c % 16) / 16.0f; - float tex_y = (float)(c / 16) / 16.0f; - float tex_x2 = tex_x + (float)this_width / (float)m_tex.width(); - float tex_y2 = tex_y + texcoord_dy; - FontVertLayout v; - v.r = term_colours[col_fg].r; - v.g = term_colours[col_fg].g; - v.b = term_colours[col_fg].b; - v.a = 255; - - v.pos_x = adv.x; - v.pos_y = adv.y; - v.tex_x = tex_x; - v.tex_y = tex_y; - verts.push_back(v); - - v.pos_x = adv.x; - v.pos_y = adv.y + m_max_advance.y; - v.tex_x = tex_x; - v.tex_y = tex_y2; - verts.push_back(v); - - v.pos_x = adv.x + this_width; - v.pos_y = adv.y + m_max_advance.y; - v.tex_x = tex_x2; - v.tex_y = tex_y2; - verts.push_back(v); - - v.pos_x = adv.x + this_width; - v.pos_y = adv.y; - v.tex_x = tex_x2; - v.tex_y = tex_y; - verts.push_back(v); - } - - i++; - adv.x += m_glyphs[c].advance - m_glyphs[c].offset; - } - - adv.x = 0; - adv.y += m_max_advance.y; - } - - if (!verts.size()) - return; - - GLState state; - state.array_vertex = true; - state.array_texcoord = true; - state.blend = true; - state.texture = true; - glmanager->set(state); - m_tex.bind(); - - GLPrimitive prim( sizeof(FontVertLayout), verts.size(), 2, - &verts[0].pos_x, - NULL, - &verts[0].tex_x); - - // Defaults to GLW_QUADS - GLW_3VF trans(x_pos, y_pos, 0.0f); - - if (drop_shadow) - { - GLW_3VF color(0.0f, 0.0f, 0.0f); - glmanager->set_current_color(color); - - trans.x++; - trans.y++; - glmanager->set_transform(&trans); - glmanager->draw_primitive(prim); - trans.x--; - trans.y--; - - color.set(1.0f, 1.0f, 1.0f); - glmanager->set_current_color(color); - } - - // TODO: Review this to see if turning array color on and off - // here is really necessary ... - state.array_colour = true; - glmanager->set(state); - prim.colour_pointer = &verts[0].r; - glmanager->set_transform(&trans); - glmanager->draw_primitive(prim); - state.array_colour = false; - glmanager->set(state); -} -struct box_vert -{ - float x; - float y; - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -}; - -static void _draw_box(int x_pos, int y_pos, float width, float height, - float box_width, unsigned char box_colour, - unsigned char box_alpha) -{ - box_vert verts[4]; - for (unsigned int i = 0; i < 4; i++) - { - verts[i].r = term_colours[box_colour].r; - verts[i].g = term_colours[box_colour].g; - verts[i].b = term_colours[box_colour].b; - verts[i].a = box_alpha; - } - verts[0].x = x_pos - box_width; - verts[0].y = y_pos - box_width; - verts[1].x = verts[0].x; - verts[1].y = y_pos + height + box_width; - verts[2].x = x_pos + width + box_width; - verts[2].y = verts[1].y; - verts[3].x = verts[2].x; - verts[3].y = verts[0].y; - - // Load identity matrix - glmanager->set_transform(); - - GLState state; - state.array_vertex = true; - state.array_colour = true; - state.blend = true; - glmanager->set(state); - - GLPrimitive prim( sizeof(box_vert), sizeof(verts) / sizeof(box_vert), 2, - &verts[0].x, - &verts[0].r, - NULL); - - glmanager->draw_primitive(prim); -} - -unsigned int FTFont::string_height(const formatted_string &str) -{ - std::string temp = str.tostring(); - return string_height(temp.c_str()); -} - -unsigned int FTFont::string_height(const char *text) -{ - int height = 1; - for (const char *itr = text; (*itr); itr++) - if (*itr == '\n') - height++; - - return char_height() * height; -} - -unsigned int FTFont::string_width(const formatted_string &str) -{ - std::string temp = str.tostring(); - return string_width(temp.c_str()); -} - -unsigned int FTFont::string_width(const char *text) -{ - unsigned int base_width = std::max(-m_min_offset, 0); - unsigned int max_width = 0; - - unsigned int width = base_width; - unsigned int adjust = 0; - for (const unsigned char *itr = (unsigned const char *)text; *itr; itr++) - { - if (*itr == '\n') - { - max_width = std::max(width + adjust, max_width); - width = base_width; - adjust = 0; - } - else - { - width += m_glyphs[*itr].advance; - adjust = std::max(0, m_glyphs[*itr].width - m_glyphs[*itr].advance); - } - } - - max_width = std::max(width + adjust, max_width); - return max_width; -} - -int FTFont::find_index_before_width(const char *text, int max_width) -{ - int width = std::max(-m_min_offset, 0); - - for (int i = 0; text[i]; i++) - { - unsigned char c = text[i]; - width += m_glyphs[c].advance; - int adjust = std::max(0, m_glyphs[c].width - m_glyphs[c].advance); - if (width + adjust > max_width) - return i; - } - - return -1; -} - -formatted_string FTFont::split(const formatted_string &str, - unsigned int max_width, unsigned int max_height) -{ - int max_lines = max_height / char_height(); - - if (max_lines < 1) - return formatted_string(); - - formatted_string ret; - ret += str; - - std::string base = str.tostring(); - int num_lines = 0; - - char *line = &base[0]; - while (true) - { - int line_end = find_index_before_width(line, max_width); - if (line_end == -1) - break; - - int space_idx = 0; - for (char *search = &line[line_end]; search > line; search--) - { - if (*search == ' ') - { - space_idx = search - line; - break; - } - } - - if (++num_lines >= max_lines || !space_idx) - { - int ellipses; - if (space_idx && (space_idx - line_end > 2)) - ellipses = space_idx; - else - ellipses = line_end - 2; - - size_t idx = &line[ellipses] - &base[0]; - ret[idx] = '.'; - ret[idx+1] = '.'; - - return ret.substr(0, idx + 2); - } - else - { - line[space_idx] = '\n'; - ret[&line[space_idx] - &base[0]] = '\n'; - } - - line = &line[space_idx+1]; - } - - return ret; -} - -void FTFont::render_string(unsigned int px, unsigned int py, - const char *text, - const coord_def &min_pos, const coord_def &max_pos, - unsigned char font_colour, bool drop_shadow, - unsigned char box_alpha, - unsigned char box_colour, - unsigned int outline, - bool tooltip) -{ - ASSERT(text); - - // Determine extent of this text - unsigned int max_rows = 1; - unsigned int cols = 0; - unsigned int max_cols = 0; - for (const char *itr = text; *itr; itr++) - { - cols++; - max_cols = std::max(cols, max_cols); - - // NOTE: only newlines should be used for tool tips. Don't use EOL. - ASSERT(*itr != '\r'); - - if (*itr == '\n') - { - cols = 0; - max_rows++; - } - } - - // Create the text block - unsigned char *chars = (unsigned char *)alloca(max_rows * max_cols); - unsigned char *colours = (unsigned char *)alloca(max_rows * max_cols); - memset(chars, ' ', max_rows * max_cols); - memset(colours, font_colour, max_rows * max_cols); - - // Fill the text block - cols = 0; - unsigned int rows = 0; - for (const char *itr = text; *itr; itr++) - { - chars[cols + rows * max_cols] = *itr; - cols++; - - if (*itr == '\n') - { - cols = 0; - rows++; - } - } - - // Find a suitable location on screen - const int buffer = 5; // additional buffer size from edges - - int wx = string_width(text); - int wy = max_rows * char_height(); - - int sx, sy; // box starting location, uses extra buffer - int tx, ty; // text starting location - - tx = px - wx / 2; - sx = tx - buffer; - if (tooltip) - { - sy = py + outline; - ty = sy + buffer; - } - else - { - ty = py - wy - outline; - sy = ty - buffer; - } - // box ending position - int ex = tx + wx + buffer; - int ey = ty + wy + buffer; - - if (ex > max_pos.x) - tx += max_pos.x - ex; - else if (sx < min_pos.x) - tx -= sx - min_pos.x; - - if (ey > max_pos.y) - ty += max_pos.y - ey; - else if (sy < min_pos.y) - ty -= sy - min_pos.y; - - if (box_alpha != 0) - _draw_box(tx, ty, wx, wy, outline, box_colour, box_alpha); - - render_textblock(tx, ty, chars, colours, max_cols, max_rows, drop_shadow); -} - -void FTFont::store(FontBuffer &buf, float &x, float &y, - const std::string &str, const VColour &col) -{ - store(buf, x, y, str, col, x); -} - -void FTFont::store(FontBuffer &buf, float &x, float &y, - const std::string &str, const VColour &col, float orig_x) -{ - for (unsigned int i = 0; i < str.size(); i++) - { - char c = str[i]; - if (c == '\n') - { - x = orig_x; - y += m_max_advance.y; - } - else - { - store(buf, x, y, c, col); - } - } -} - -void FTFont::store(FontBuffer &buf, float &x, float &y, - const formatted_string &fs) -{ - store(buf, x, y, fs, x); -} - -void FTFont::store(FontBuffer &buf, float &x, float &y, - const formatted_string &fs, float orig_x) -{ - int colour = LIGHTGREY; - for (unsigned int i = 0; i < fs.ops.size(); i++) - { - switch (fs.ops[i].type) - { - case FSOP_COLOUR: - // Only foreground colors for now... - colour = fs.ops[i].x & 0xF; - break; - case FSOP_TEXT: - store(buf, x, y, fs.ops[i].text, term_colours[colour], orig_x); - break; - default: - break; - } - } -} +#ifdef USE_FT +#include "fontwrapper-ft.h" +#endif -void FTFont::store(FontBuffer &buf, float &x, float &y, - unsigned char c, const VColour &col) +FontWrapper* FontWrapper::create() { - if (!m_glyphs[c].renderable) - { - x += m_glyphs[c].advance; - return; - } - - int this_width = m_glyphs[c].width; - - float pos_sx = x + m_glyphs[c].offset; - float pos_sy = y; - float pos_ex = pos_sx + this_width; - float pos_ey = y + m_max_advance.y; - - float tex_sx = (float)(c % 16) / 16.0f; - float tex_sy = (float)(c / 16) / 16.0f; - float tex_ex = tex_sx + (float)this_width / (float)m_tex.width(); - float tex_ey = tex_sy + (float)m_max_advance.y / (float)m_tex.height(); - - { - PTCVert &v = buf.get_next(); - v.col = col; - v.pos_x = pos_sx; - v.pos_y = pos_sy; - v.tex_x = tex_sx; - v.tex_y = tex_sy; - } - { - PTCVert &v = buf.get_next(); - v.col = col; - v.pos_x = pos_sx; - v.pos_y = pos_ey; - v.tex_x = tex_sx; - v.tex_y = tex_ey; - } - { - PTCVert &v = buf.get_next(); - v.col = col; - v.pos_x = pos_ex; - v.pos_y = pos_ey; - v.tex_x = tex_ex; - v.tex_y = tex_ey; - } - { - PTCVert &v = buf.get_next(); - v.col = col; - v.pos_x = pos_ex; - v.pos_y = pos_sy; - v.tex_x = tex_ex; - v.tex_y = tex_sy; - } - - x += m_glyphs[c].advance; +#ifdef USE_FT + return (FontWrapper *) new FTFontWrapper(); +#endif } -#endif +#endif // USE_TILE |