+
+ UTF8Iterator& operator++() {
+ try {
+ chr = decode_utf8(text, pos);
+ } catch (std::exception) {
+ log_debug << "Malformed utf-8 sequence beginning with " << *((uint32_t*)(text.c_str() + pos)) << " found " << std::endl;
+ chr = 0;
+ ++pos;
+ }
+
+ return *this;
+ }
+
+ uint32_t operator*() const {
+ return chr;
+ }
+};
+
+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(GlyphWidth glyph_width_,
+ const std::string& filename,
+ int shadowsize_)
+: glyph_width(glyph_width_),
+ shadowsize(shadowsize_),
+ glyphs(65536)
+{
+ for(unsigned int i=0; i<65536;i++) glyphs[i].surface_idx = -1;
+
+ const std::string fontdir = FileSystem::dirname(filename);
+ const std::string fontname = FileSystem::basename(filename);
+
+ // scan for prefix-filename in addons search path
+ char **rc = PHYSFS_enumerateFiles(fontdir.c_str());
+ for (char **i = rc; *i != NULL; i++) {
+ std::string filename(*i);
+ if( filename.rfind(fontname) != std::string::npos ) {
+ loadFontFile(fontdir + filename);
+ }
+ }
+ PHYSFS_freeList(rc);
+}
+
+void
+Font::loadFontFile(const std::string &filename)
+{
+ lisp::Parser parser;
+ log_debug << "Loading font: " << filename << std::endl;
+ const lisp::Lisp* root = parser.parse(filename);
+ const lisp::Lisp* config_l = root->get_lisp("supertux-font");
+
+ if(!config_l) {
+ std::ostringstream msg;
+ msg << "Font file:" << filename << ": is not a supertux-font file";
+ throw std::runtime_error(msg.str());
+ }
+
+ int def_char_width=0;
+
+ if( !config_l->get("glyph-width",def_char_width) ) {
+ log_warning << "Font:"<< filename << ": misses default glyph-width" << std::endl;
+ }
+
+ if( !config_l->get("glyph-height",char_height) ) {
+ std::ostringstream msg;
+ msg << "Font:" << filename << ": misses glyph-height";
+ throw std::runtime_error(msg.str());
+ }
+
+ lisp::ListIterator iter(config_l);
+ while(iter.next()) {
+ const std::string& token = iter.item();
+ if( token == "surface" ) {
+ const lisp::Lisp * glyphs_val = iter.lisp();
+ int local_char_width;
+ bool monospaced;
+ GlyphWidth local_glyph_width;
+ std::string glyph_image;
+ std::string shadow_image;
+ std::vector<std::string> chars;
+ if( ! glyphs_val->get("glyph-width", local_char_width) ) {
+ local_char_width = def_char_width;
+ }
+ if( ! glyphs_val->get("monospace", monospaced ) ) {
+ local_glyph_width = glyph_width;
+ }
+ else {
+ if( monospaced ) local_glyph_width = FIXED;
+ else local_glyph_width = VARIABLE;
+ }
+ if( ! glyphs_val->get("glyphs", glyph_image) ) {
+ std::ostringstream msg;
+ msg << "Font:" << filename << ": missing glyphs image";
+ throw std::runtime_error(msg.str());
+ }
+ if( ! glyphs_val->get("shadows", shadow_image) ) {
+ std::ostringstream msg;
+ msg << "Font:" << filename << ": missing shadows image";
+ throw std::runtime_error(msg.str());
+ }
+ if( ! glyphs_val->get("chars", chars) || chars.size() == 0) {
+ std::ostringstream msg;
+ msg << "Font:" << filename << ": missing chars definition";
+ throw std::runtime_error(msg.str());
+ }
+
+ if( local_char_width==0 ) {
+ std::ostringstream msg;
+ msg << "Font:" << filename << ": misses glyph-width for some surface";
+ throw std::runtime_error(msg.str());
+ }
+
+ loadFontSurface(glyph_image, shadow_image, chars,
+ local_glyph_width, local_char_width);
+ }
+ }
+}
+
+void
+Font::loadFontSurface(
+ const std::string &glyphimage,
+ const std::string &shadowimage,
+ const std::vector<std::string> &chars,
+ GlyphWidth glyph_width,
+ int char_width
+ )
+{
+ Surface glyph_surface = Surface("images/engine/fonts/" + glyphimage);
+ Surface shadow_surface = Surface("images/engine/fonts/" + shadowimage);
+
+ int surface_idx = glyph_surfaces.size();
+ glyph_surfaces.push_back(glyph_surface);
+ shadow_surfaces.push_back(shadow_surface);
+
+ int row=0, col=0;
+ int wrap = glyph_surface.get_width() / char_width;
+
+ SDL_Surface *surface = NULL;
+
+ if( glyph_width == VARIABLE ) {
+ //this does not work:
+ // surface = ((SDL::Texture *)glyph_surface.get_texture())->get_texture();
+ surface = IMG_Load_RW(get_physfs_SDLRWops("images/engine/fonts/"+glyphimage), 1);
+ if(surface == NULL) {
+ std::ostringstream msg;
+ msg << "Couldn't load image '" << glyphimage << "' :" << SDL_GetError();
+ throw std::runtime_error(msg.str());
+ }
+ SDL_LockSurface(surface);
+ }
+
+ for( unsigned int i = 0; i < chars.size(); i++) {
+ for(UTF8Iterator chr(chars[i]); !chr.done(); ++chr) {
+ int y = row * char_height;
+ int x = col * char_width;
+ if( ++col == wrap ) { col=0; row++; }
+ if( *chr == 0x0020 && glyphs[0x20].surface_idx != -1) continue;
+
+ Glyph glyph;
+ glyph.surface_idx = surface_idx;
+
+ if( glyph_width == FIXED ) {
+ glyph.rect = Rect(x, y, x + char_width, y + char_height);
+ glyph.offset = Vector(0, 0);
+ glyph.advance = char_width;
+ }
+ else {
+ int left = x;
+ while (left < x + char_width && vline_empty(surface, left, y, y + char_height, 64))
+ left += 1;
+ int right = x + char_width - 1;
+ while (right > left && vline_empty(surface, right, y, y + char_height, 64))
+ right -= 1;
+
+ if (left <= right)
+ glyph.rect = Rect(left, y, right+1, y + char_height);
+ else // glyph is completely transparent
+ glyph.rect = Rect(x, y, x + char_width, y + char_height);
+
+ glyph.offset = Vector(0, 0);
+ glyph.advance = glyph.rect.get_width() + 1; // FIXME: might be useful to make spacing configurable
+ }
+
+ glyphs[*chr] = glyph;
+ }
+ if( col>0 && col <= wrap ) {
+ col = 0;
+ row++;
+ }
+ }
+
+ if( surface != NULL ) {
+ SDL_UnlockSurface(surface);
+ SDL_FreeSurface(surface);
+ }