File: FontChain.cpp

package info (click to toggle)
vcmi 1.6.5%2Bdfsg-2
  • links: PTS, VCS
  • area: contrib
  • in suites: forky, sid, trixie
  • size: 32,060 kB
  • sloc: cpp: 238,971; python: 265; sh: 224; xml: 157; ansic: 78; objc: 61; makefile: 49
file content (160 lines) | stat: -rw-r--r-- 4,889 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
 * FontChain.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
 * License: GNU General Public License v2.0 or later
 * Full text of license available in license.txt file, in main folder
 *
 */
#include "StdInc.h"
#include "FontChain.h"

#include "CTrueTypeFont.h"
#include "CBitmapFont.h"

#include "../CGameInfo.h"

#include "../../lib/CConfigHandler.h"
#include "../../lib/modding/CModHandler.h"
#include "../../lib/texts/TextOperations.h"
#include "../../lib/texts/CGeneralTextHandler.h"
#include "../../lib/texts/Languages.h"

void FontChain::renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const
{
	auto chunks = splitTextToChunks(data);
	int maxAscent = getFontAscentScaled();
	Point currentPos = pos;
	for (auto const & chunk : chunks)
	{
		Point chunkPos = currentPos;
		int currAscent = chunk.font->getFontAscentScaled();
		chunkPos.y += maxAscent - currAscent;
		chunk.font->renderText(surface, chunk.text, color, chunkPos);
		currentPos.x += chunk.font->getStringWidthScaled(chunk.text);
	}
}

size_t FontChain::getFontAscentScaled() const
{
	size_t maxHeight = 0;
	for(const auto & font : chain)
		maxHeight = std::max(maxHeight, font->getFontAscentScaled());
	return maxHeight;
}

bool FontChain::bitmapFontsPrioritized(const std::string & bitmapFontName) const
{
	const std::string & fontType = settings["video"]["fontsType"].String();
	if (fontType == "original")
		return true;
	if (fontType == "scalable")
		return false;

	// else - autoselection.

	if (getScalingFactor() != 1)
		return false; // If xbrz in use ttf/scalable fonts are preferred

	if (!vstd::isAlmostEqual(1.0, settings["video"]["fontScalingFactor"].Float()))
		return false; // If player requested non-100% scaling - use scalable fonts

	std::string gameLanguage = CGI->generaltexth->getPreferredLanguage();
	std::string gameEncoding = Languages::getLanguageOptions(gameLanguage).encoding;
	std::string fontEncoding = CGI->modh->findResourceEncoding(ResourcePath("data/" + bitmapFontName, EResType::BMP_FONT));

	// player uses language with different encoding than his bitmap fonts
	// for example, Polish language with English fonts or Chinese language which can't use H3 fonts at all
	// this may result in unintended mixing of ttf and bitmap fonts, which may have a bit different look
	// so in this case prefer ttf fonts that are likely to cover target language better than H3 fonts
	if (fontEncoding != gameEncoding)
		return false;

	return true; // else - use original bitmap fonts
}

void FontChain::addTrueTypeFont(const JsonNode & trueTypeConfig)
{
	chain.insert(chain.begin(), std::make_unique<CTrueTypeFont>(trueTypeConfig));
}

void FontChain::addBitmapFont(const std::string & bitmapFilename)
{
	if (bitmapFontsPrioritized(bitmapFilename))
		chain.insert(chain.begin(), std::make_unique<CBitmapFont>(bitmapFilename));
	else
		chain.push_back(std::make_unique<CBitmapFont>(bitmapFilename));
}

bool FontChain::canRepresentCharacter(const char * data) const
{
	for(const auto & font : chain)
		if (font->canRepresentCharacter(data))
			return true;
	return false;
}

size_t FontChain::getLineHeightScaled() const
{
	size_t maxHeight = 0;
	for(const auto & font : chain)
		maxHeight = std::max(maxHeight, font->getLineHeightScaled());
	return maxHeight;
}

size_t FontChain::getGlyphWidthScaled(const char * data) const
{
	for(const auto & font : chain)
		if (font->canRepresentCharacter(data))
			return font->getGlyphWidthScaled(data);
	return 0;
}

std::vector<FontChain::TextChunk> FontChain::splitTextToChunks(const std::string & data) const
{
	// U+FFFD - replacement character (question mark in rhombus)
	static const std::string replacementCharacter = u8"�";

	std::vector<TextChunk> chunks;

	const auto & selectFont = [this](const char * characterPtr) -> const IFont *
	{
		for(const auto & font : chain)
			if (font->canRepresentCharacter(characterPtr))
				return font.get();
		return nullptr;
	};

	for (size_t i = 0; i < data.size(); i += TextOperations::getUnicodeCharacterSize(data[i]))
	{
		std::string symbol = data.substr(i, TextOperations::getUnicodeCharacterSize(data[i]));
		const IFont * currentFont = selectFont(symbol.data());

		if (currentFont == nullptr)
		{
			symbol = replacementCharacter;
			currentFont = selectFont(symbol.data());
		}

		if (currentFont == nullptr)
			continue; // Still nothing - neither desired character nor fallback can be rendered

		if (chunks.empty() || chunks.back().font != currentFont)
			chunks.push_back({currentFont, symbol});
		else
			chunks.back().text += symbol;
	}

	return chunks;
}

size_t FontChain::getStringWidthScaled(const std::string & data) const
{
	size_t result = 0;
	auto chunks = splitTextToChunks(data);
	for (auto const & chunk : chunks)
		result += chunk.font->getStringWidthScaled(chunk.text);

	return result;
}