/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include "core/raster_font.h" #include "core/render_resource_factory.h" #include "metafile/nml.h" #include "log/log.h" using namespace GS; using namespace GS::Render; //------------------------------------------------------------------------------ float RasterFont::GetHeight(bool normalized) const { if (normalized) return height; return GetPage(0) ? height * GetPage(0)->GetWidth() : -1; } float RasterFont::GetBaseline(bool normalized) const { if (normalized) return baseline; return GetPage(0) ? baseline * GetPage(0)->GetWidth() : -1; } Vector2 RasterFont::ComputeLineRect(const char *s, bool normalized) const { Vector2 r(0, height); for ( ; s[0] && s[0] != '\n'; ++s) if (const Glyph *c = GetGlyphInfos(s[0])) r.x += c->step; if (!normalized) { if (GetPage(0)) { r.x *= GetPage(0)->GetWidth(); r.y *= GetPage(0)->GetHeight(); } else r.Set(-1, -1); } return r; } Vector2 RasterFont::ComputeStringRect(const char *s, bool normalized) const { Vector2 r(0, 0); forever { float w = 0; // Line width. for ( ; s[0] && s[0] != '\n'; ++s) if (const Glyph *c = GetGlyphInfos(s[0])) w += c->step; if (w > r.x) // Largest width. r.x = w; r.y += height; // Line height. if (!s[0]) break; ++s; // Jump over the line feed. } if (!normalized) { if (GetPage(0)) { r.x *= GetPage(0)->GetWidth(); r.y *= GetPage(0)->GetHeight(); } else r.Set(-1, -1); } return r; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Texture *RasterFont::GetPage(uint page) const { return (page < pages.GetCount()) ? pages.ObjectAt(page) : NULL; } const RasterFont::Glyph *RasterFont::GetGlyphInfos(uchar index) const { return glyph[index].available ? &glyph[index] : NULL; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool RasterFont::Load(ResourceFactory &rf, const char *nml, const char *base) { Unload(); //----------------------------------------------------------- #define RFL_ERROR(l) { (l); Unload(); return false; } //----------------------------------------------------------- using namespace GS::NML; File file; if (!Parser::Load(nml, file)) RFL_ERROR(__LOG_E__ << "Failed to load raster font description file '" << nml << "'.\n") // Load font settings. Tag *root = file.GetTag("Font:Common;"); if (!root) RFL_ERROR(__LOG_E__ << "Missing base tag in font description ('" << nml << "').\n") uint page_count = 1; NMLTagForeach(tag, *root) { if (tag->name == "Height") height = tag->GetReal(); else if (tag->name == "BaseLine") baseline = tag->GetReal(); else if (tag->name == "PageCount") page_count = tag->GetInteger(); else __LOG_W__ << "Unexpected tag in font description header ('" << tag->name << "').\n"; } // Parse glyphs... root = file.GetTag("Font;"); NMLTagForeach(tag, *root) if (tag->name == "Char") { Tag *wrk = tag->GetTag("Id;"); if (!wrk || (wrk->GetType() != Variant::VariantInteger)) { __LOG_W__ << "Glyph with no or invalid ID in font '" << nml << "', skipping.\n"; continue; } int glyphidx = wrk->GetInteger(); if (glyphidx > 255) continue; Glyph *pglyph = &glyph[glyphidx]; pglyph->page = 0; pglyph->available = true; NMLTagForeach(wrk, *tag) { if (wrk->name == "U") pglyph->u = wrk->GetReal(); else if (wrk->name == "V") pglyph->v = wrk->GetReal(); else if (wrk->name == "W") pglyph->w = wrk->GetReal(); else if (wrk->name == "H") pglyph->h = wrk->GetReal(); else if (wrk->name == "OffsetU") pglyph->offx = wrk->GetReal(); else if (wrk->name == "OffsetV") pglyph->offy = wrk->GetReal(); else if (wrk->name == "Step") pglyph->step = wrk->GetReal(); else if (wrk->name == "Page") { if ((wrk->GetType() == Variant::VariantInteger) && ((uint)wrk->GetInteger() < page_count)) pglyph->page = wrk->GetInteger(); else { __LOG_W__ << "Invalid glyph page in font '" << nml << "' (Glyph #" << glyphidx << "), skipping.\n"; pglyph->available = false; break; } } } } // Sanity check... if (!glyph[' '].available) __LOG_W__ << "No space character in font '" << nml << "'.\n"; // Load font pages. for (uint n = 0; n < page_count; n++) if (Texture *t = rf.LoadTexture(String::Format("%s_%02d.tga", base, n))) pages.Add(t); name = nml; return true; } void RasterFont::Unload() { pages.Clear(); for (uint n = 0; n < 256; n++) glyph[n].Reset(); } //------------------------------------------------------------------------------