From: Ingo Ruhnke Date: Sun, 28 Jan 2007 16:45:18 +0000 (+0000) Subject: - added support for variable width fonts (not fully finished, needs some more cleanup... X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=7504b8ef1155259916f0e38eeb74a6024bb1d85b;p=supertux.git - added support for variable width fonts (not fully finished, needs some more cleanup, but mostly working) SVN-Revision: 4708 --- diff --git a/src/resources.cpp b/src/resources.cpp index 215d6d5e6..a4a3f80a1 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -45,29 +45,30 @@ void load_shared() MouseCursor::set_current(mouse_cursor); /* Load global images: */ - gold_text = new Font("images/engine/fonts/gold.png", - "images/engine/fonts/shadow.png", 16, 18); - blue_text = new Font("images/engine/fonts/blue.png", - "images/engine/fonts/shadow.png", 16, 18, 3); - white_text = new Font("images/engine/fonts/white.png", + gold_text = new Font("images/engine/fonts/gold.png", "images/engine/fonts/shadow.png", 16, 18); - gray_text = new Font("images/engine/fonts/gray.png", + blue_text = new Font("images/engine/fonts/blue.png", + "images/engine/fonts/shadow.png", 16, 18, 3); + // white_text = new Font("images/engine/fonts/white.png", + // "images/engine/fonts/shadow.png", 16, 18); + white_text = new Font("images/engine/fonts/white.png", 16, 18); + gray_text = new Font("images/engine/fonts/gray.png", "images/engine/fonts/shadow.png", 16, 18); white_small_text = new Font("images/engine/fonts/white-small.png", "images/engine/fonts/shadow-small.png", 8, 9, 1); white_big_text = new Font("images/engine/fonts/white-big.png", "images/engine/fonts/shadow-big.png", 20, 22, 3); - Menu::default_font = white_text; - Menu::active_font = blue_text; + Menu::default_font = white_text; + Menu::active_font = blue_text; Menu::deactive_font = gray_text; - Menu::label_font = white_big_text; - Menu::field_font = gold_text; + Menu::label_font = white_big_text; + Menu::field_font = gold_text; Button::info_font = white_small_text; sprite_manager = new SpriteManager(); - tile_manager = new TileManager("images/tiles.strf"); + tile_manager = new TileManager("images/tiles.strf"); /* Tuxes: */ char img_name[1024]; diff --git a/src/video/font.cpp b/src/video/font.cpp index 07e560626..640ee3add 100644 --- a/src/video/font.cpp +++ b/src/video/font.cpp @@ -23,6 +23,9 @@ #include #include +#include +#include "physfs/physfs_sdl.hpp" + #include "lisp/parser.hpp" #include "lisp/lisp.hpp" #include "screen.hpp" @@ -30,75 +33,144 @@ #include "drawing_context.hpp" #include "log.hpp" +namespace { +bool has_multibyte_mark(unsigned char c); +uint32_t decode_utf8(const std::string& text, size_t& p); + +bool vline_empty(SDL_Surface* surface, int x, int start_y, int end_y, Uint8 threshold) +{ + Uint8* pixels = (Uint8*)surface->pixels; + + for(int y = start_y; y < end_y; ++y) + { + const Uint8& p = pixels[surface->pitch*y + x*surface->format->BytesPerPixel + 3]; + if (p > threshold) + { + return false; + } + } + return true; +} +} // namespace + Font::Font(const std::string& file, const std::string& shadowfile, int w, int h, int shadowsize) - : chars(0), shadow_chars(0), w(w), h(h), shadowsize(shadowsize) + : glyph_surface(0), shadow_chars(0), + char_width(w), char_height(h), + shadowsize(shadowsize) { - chars = new Surface(file); - shadow_chars = new Surface(shadowfile); + glyph_surface = new Surface(file); + shadow_chars = new Surface(shadowfile); first_char = 32; - char_count = ((int) chars->get_height() / h) * 16; + char_count = ((int) glyph_surface->get_height() / char_height) * 16; + + for(uint32_t i = 0; i < char_count; ++i) + { + float x = (i % 16) * char_width; + float y = (i / 16) * char_height; + glyphs.push_back(Rect(x, y, + x + char_width, y + char_height)); + } +} + +Font::Font(const std::string& filename, int char_width_, int char_height_) + : glyph_surface(0), shadow_chars(0), + char_width(char_width_), char_height(char_height_), + shadowsize(0) +{ + glyph_surface = new Surface(filename); + + first_char = 32; + char_count = ((int) glyph_surface->get_height() / char_height) * 16; + + // Load the surface into RAM and scan the pixel data for characters + SDL_Surface* surface = IMG_Load_RW(get_physfs_SDLRWops(filename), 1); + if(surface == NULL) { + std::ostringstream msg; + msg << "Couldn't load image '" << filename << "' :" << SDL_GetError(); + throw std::runtime_error(msg.str()); + } + + SDL_LockSurface(surface); + + for(uint32_t i = 0; i < char_count; ++i) + { + int x = (i % 16) * char_width; + int y = (i / 16) * char_height; + + int left = x; + while (left < x + char_width && + vline_empty(surface, left, y, y + char_height, 0)) + left += 1; + + int right = x + char_width - 1; + while (right > left && + vline_empty(surface, right, y, y + char_height, 0)) + right -= 1; + + if (left <= right) + glyphs.push_back(Rect(left, y, + right+1, y + char_height)); + else // glyph is completly transparent + glyphs.push_back(Rect(x, y, + x + char_width, y + char_height)); + + } + + SDL_UnlockSurface(surface); + + SDL_FreeSurface(surface); } Font::~Font() { - delete chars; + delete glyph_surface; delete shadow_chars; } float Font::get_text_width(const std::string& text) const { - /** Let's calculate the size of the biggest paragraph */ - std::string::size_type l, hl, ol; - hl = 0; l = 0; - while(true) + float curr_width = 0; + float last_width = 0; + + // FIXME: add UTF8 decode here + for(std::string::size_type i = 0; i < text.size(); ++i) { - ol = l; - l = text.find("\n", l+1); - if(l == std::string::npos) - break; - if(hl < l-ol) - hl = l-ol; + uint32_t chr = text[i]; + if (chr == '\n') + { + last_width = std::max(last_width, curr_width); + curr_width = 0; + } + else + { + curr_width += glyphs[static_cast(text[i])].get_width() + 1; + } } - if(hl == 0) - hl = text.size(); - - for (unsigned int i = 0; i < text.size(); i++) - if ((unsigned char) text[i] > 0xC2 && (unsigned char) text[i] < 0xC6) - hl--; // control characters are a WASTE. - return hl * w; + return std::max(curr_width, last_width); } float Font::get_text_height(const std::string& text) const { - /** Let's calculate height of the text */ - std::string::size_type l, hh; - hh = h; l = 0; - while(true) + std::string::size_type text_height = char_height; + + for(std::string::size_type i = 0; i < text.size(); ++i) { - l = text.find("\n", l+1); - if(l == std::string::npos) - break; - hh += h + 2; + if (i == '\n') + text_height += char_height + 2; } - return hh; + return text_height; } float Font::get_height() const { - return h; -} - -std::string -Font::wrap_to_width(const std::string& s, int max_width, std::string* overflow) const -{ - return wrap_to_chars(s, max_width / w, overflow); + return char_height; } std::string @@ -158,7 +230,7 @@ Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment, draw_text(temp, pos + Vector(0,y), drawing_effect, alpha); i = l+1; - y += h + 2; + y += char_height + 2; } } @@ -170,9 +242,63 @@ Font::draw_text(const std::string& text, const Vector& pos, draw_chars(shadow_chars, text, pos + Vector(shadowsize, shadowsize), drawing_effect, alpha); - draw_chars(chars, text, pos, drawing_effect, alpha); + draw_chars(glyph_surface, text, pos, drawing_effect, alpha); } +void +Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos, + DrawingEffect drawing_effect, float alpha) const +{ + Vector p = pos; + size_t i = 0; + while(i < text.size()) { + uint32_t c; + try { + c = decode_utf8(text, i); // FIXME: this seems wrong, since when incrementing i by + } + catch (std::runtime_error) { + log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + i)) << " found " << std::endl; + c = 0; + i++; + } + + int font_index = c - first_char; + + // a non-printable character? + if(c == '\n') { + p.x = pos.x; + p.y += char_height + 2; + continue; + } + if(c == ' ') { + p.x += glyphs[font_index].get_width(); + continue; + } + + // we don't have the control chars 0x80-0xa0 in the font + if (c >= 0x80) { + font_index -= 32; + if(c <= 0xa0) { + log_debug << "Unsupported utf-8 character '" << c << "' found" << std::endl; + font_index = 0; + } + } + + if(font_index < 0 || font_index >= (int) char_count) { + log_debug << "Unsupported utf-8 character found" << std::endl; + font_index = 0; + } + + const Rect& glyph = glyphs[font_index]; + pchars->draw_part(glyph.get_left(), glyph.get_top(), + p.x, p.y, + glyph.get_width(), glyph.get_height(), + alpha, drawing_effect); + p.x += glyphs[font_index].get_width(); + } +} + + namespace { /** @@ -231,56 +357,4 @@ uint32_t decode_utf8(const std::string& text, size_t& p) throw std::runtime_error("Malformed utf-8 sequence"); } -} - -void -Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos, - DrawingEffect drawing_effect, float alpha) const -{ - Vector p = pos; - size_t i = 0; - while(i < text.size()) { - uint32_t c; - try { - c = decode_utf8(text, i); - } - catch (std::runtime_error) { - log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + i)) << " found " << std::endl; - c = 0; - i++; - } - int font_index; - - // a non-printable character? - if(c == '\n') { - p.x = pos.x; - p.y += h + 2; - continue; - } - if(c == ' ') { - p.x += w; - continue; - } - - font_index = c - first_char; - // we don't have the control chars 0x80-0xa0 in the font - if(c >= 0x80) { - font_index -= 32; - if(c <= 0xa0) { - log_debug << "Unsupported utf-8 character '" << c << "' found" << std::endl; - font_index = 0; - } - } - - if(font_index < 0 || font_index >= (int) char_count) { - log_debug << "Unsupported utf-8 character found" << std::endl; - font_index = 0; - } - - int source_x = (font_index % 16) * w; - int source_y = (font_index / 16) * h; - pchars->draw_part(source_x, source_y, p.x, p.y, w, h, alpha, - drawing_effect); - p.x += w; - } -} +} // namespace diff --git a/src/video/font.hpp b/src/video/font.hpp index fffcc1fa4..c30dba215 100644 --- a/src/video/font.hpp +++ b/src/video/font.hpp @@ -25,6 +25,7 @@ #include "video/surface.hpp" #include "math/vector.hpp" +#include "math/rect.hpp" enum FontAlignment { LEFT_ALLIGN, @@ -35,8 +36,22 @@ enum FontAlignment { class Font { public: + /** Construct a fixed-width font + * + * @param file image file containing the characters + * @param shadowfile image file containing the characters shadows + * @param w width of a character + * @param h height of a character + */ Font(const std::string& file, const std::string& shadowfile, int w, int h, int shadowsize = 2); + + /** Construct a variable-width font + * + * @param file image file containing the characters + */ + Font(const std::string& file, int char_width, int char_height); + ~Font(); /** returns the width of a given text. (Note that I won't add a normal @@ -51,13 +66,11 @@ public: * just use get_height(). */ float get_text_height(const std::string& text) const; - /// returns the height of the font. - float get_height() const; /** - * returns the given string, truncated (preferrably at whitespace) to be at most max_width pixels long + * returns the height of the font. */ - std::string wrap_to_width(const std::string& text, int max_width, std::string* overflow) const; + float get_height() const; /** * returns the given string, truncated (preferrably at whitespace) to be at most max_chars characters long @@ -82,16 +95,19 @@ private: const Vector& position, DrawingEffect drawing_effect, float alpha) const; - Surface* chars; + Surface* glyph_surface; Surface* shadow_chars; - int w; - int h; + int char_width; + int char_height; int shadowsize; /// the number of the first character that is represented in the font uint32_t first_char; /// the number of the last character that is represented in the font uint32_t char_count; + + /** Location of the characters inside the surface */ + std::vector glyphs; }; #endif