Beispiel #1
0
std::shared_ptr<ApocalypseFont> ApocalypseFont::loadFont(Framework &fw,
                                                         tinyxml2::XMLElement *fontElement)
{
	int spacewidth = 0;
	int height = 0;
	int width = 0;
	UString fileName;
	UString fontName;

	const char *attr = fontElement->Attribute("name");
	if (!attr)
	{
		LogError("apocfont element with no \"name\" attribute");
		return nullptr;
	}
	fontName = attr;
	attr = fontElement->Attribute("path");
	if (!attr)
	{
		LogError("apocfont \"%s\" with no \"path\" attribute", fontName.c_str());
		return nullptr;
	}
	fileName = attr;

	auto err = fontElement->QueryIntAttribute("height", &height);
	if (err != tinyxml2::XML_NO_ERROR || height <= 0)
	{
		LogError("apocfont \"%s\" with invalid \"height\" attribute", fontName.c_str());
		return nullptr;
	}
	err = fontElement->QueryIntAttribute("width", &width);
	if (err != tinyxml2::XML_NO_ERROR || width <= 0)
	{
		LogError("apocfont \"%s\" with invalid \"width\" attribute", fontName.c_str());
		return nullptr;
	}
	err = fontElement->QueryIntAttribute("spacewidth", &spacewidth);
	if (err != tinyxml2::XML_NO_ERROR || spacewidth <= 0)
	{
		LogError("apocfont \"%s\" with invalid \"spacewidth\" attribute", fontName.c_str());
		return nullptr;
	}

	auto file = fw.data->load_file(fileName);
	if (!file)
	{
		LogError("apocfont \"%s\" - Failed to open font path \"%s\"", fontName.c_str(),
		         fileName.c_str());
		return nullptr;
	}

	auto fileSize = file.size();
	int glyphSize = height * width;
	int glyphCount = fileSize / glyphSize;

	if (!glyphCount)
	{
		LogError("apocfont \"%s\" - file \"%s\" contains no glyphs", fontName.c_str(),
		         fileName.c_str());
	}
	std::shared_ptr<ApocalypseFont> font(new ApocalypseFont);

	font->name = fontName;
	font->spacewidth = spacewidth;
	font->fontheight = height;
	font->averagecharacterwidth = 0;
	font->palette = fw.data->load_palette(fontElement->Attribute("palette"));

	for (auto *glyphNode = fontElement->FirstChildElement(); glyphNode;
	     glyphNode = glyphNode->NextSiblingElement())
	{
		UString nodeName = glyphNode->Name();
		if (nodeName != "glyph")
		{
			LogError("apocfont \"%s\" has unexpected child node \"%s\", skipping", fontName.c_str(),
			         nodeName.c_str());
			continue;
		}
		int offset;
		err = glyphNode->QueryIntAttribute("offset", &offset);
		if (err != tinyxml2::XML_NO_ERROR)
		{
			LogError(
			    "apocfont \"%s\" has glyph with invalid/missing offset attribute - skipping glyph",
			    fontName.c_str());
			continue;
		}
		attr = glyphNode->Attribute("string");
		if (!attr)
		{
			LogError("apocfont \"%s\" has glyph with missing string attribute - skipping glyph",
			         fontName.c_str());
			continue;
		}

		UString glyphString(attr);
		if (glyphString.length() != 1)
		{
			LogError("apocfont \"%s\" glyph w/offset %d has %d codepoints, expected one - skipping "
			         "glyph",
			         fontName.c_str(), offset, glyphString.length());
			continue;
		}
		if (offset >= glyphCount)
		{
			LogError("apocfont \"%s\" glyph \"%s\" has invalid offset %d - file contains a max of "
			         "%d - skipping glyph",
			         fontName.c_str(), glyphString.c_str(), offset, glyphCount);
			continue;
		}
		UniChar c = glyphString[0];
		if (font->fontbitmaps.find(c) != font->fontbitmaps.end())
		{
			LogError(
			    "apocfont \"%s\" glyph \"%s\" has multiple definitions - skipping re-definition",
			    fontName.c_str(), glyphString.c_str());
			continue;
		}
		file.seekg(glyphSize * offset, std::ios::beg);
		int glyphWidth = 0;

		auto glyphImage = std::make_shared<PaletteImage>(Vec2<int>(width, height));
		{
			PaletteImageLock imgLock(glyphImage, ImageLockUse::Write);

			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
				{
					uint8_t idx;
					file.read(reinterpret_cast<char *>(&idx), 1);
					imgLock.set(Vec2<int>{x, y}, idx);
					if (idx != 0 && glyphWidth < x)
						glyphWidth = x;
				}
			}
		}
		auto trimmedGlyph = std::make_shared<PaletteImage>(Vec2<int>{glyphWidth + 2, height});
		PaletteImage::blit(glyphImage, Vec2<int>{0, 0}, trimmedGlyph);
		font->fontbitmaps[c] = trimmedGlyph;

		font->averagecharacterwidth += glyphWidth + 2;
	}

	// FIXME: Bit of a hack to handle spaces?
	auto spaceImage = std::make_shared<PaletteImage>(Vec2<int>{spacewidth, height});
	// Defaults to transparent (0)
	font->fontbitmaps[UString::u8Char(' ')] = spaceImage;

	font->averagecharacterwidth /= font->fontbitmaps.size();

	return font;
}
Beispiel #2
0
sp<BitmapFont> BitmapFont::loadFont(const std::map<UniChar, UString> &glyphMap, int spaceWidth,
                                    int fontHeight, UString fontName, sp<Palette> defaultPalette)
{
	auto font = mksp<BitmapFont>();

	font->spacewidth = spaceWidth;
	font->fontheight = fontHeight;
	font->averagecharacterwidth = 0;
	font->name = fontName;
	font->palette = defaultPalette;

	size_t totalGlyphWidth = 0;

	for (auto &p : glyphMap)
	{
		auto fontImage = fw().data->loadImage(p.second);
		if (!fontImage)
		{
			LogError("Failed to read glyph image \"%s\"", p.second);
			continue;
		}
		auto paletteImage = std::dynamic_pointer_cast<PaletteImage>(fontImage);
		if (!paletteImage)
		{
			LogError("Glyph image \"%s\" doesn't look like a PaletteImage", p.second);
			continue;
		}
		unsigned int maxWidth = 0;

		// FIXME: Proper kerning
		// First find the widest non-transparent part of the glyph
		{
			PaletteImageLock imgLock(paletteImage, ImageLockUse::Read);
			for (unsigned int y = 0; y < paletteImage->size.y; y++)
			{
				for (unsigned int x = 0; x < paletteImage->size.x; x++)
				{
					if (imgLock.get({x, y}) != 0)
					{
						if (x > maxWidth)
							maxWidth = x;
					}
				}
			}
		}
		// Trim the glyph to the max non-transparent width + 2 px
		auto trimmedGlyph = mksp<PaletteImage>(Vec2<int>{maxWidth + 2, fontHeight});
		PaletteImage::blit(paletteImage, trimmedGlyph);

		font->fontbitmaps[p.first] = trimmedGlyph;
		totalGlyphWidth += trimmedGlyph->size.x;
	}

	font->averagecharacterwidth = totalGlyphWidth / font->fontbitmaps.size();

	// Always add a 'space' image:

	auto spaceImage = mksp<PaletteImage>(Vec2<int>{spaceWidth, fontHeight});
	font->fontbitmaps[UString::u8Char(' ')] = spaceImage;

	return font;
}