1 #include "Font.h"
2
3 #include "../ResourceManager.h"
4 #include "../common/misc.h"
5 #include "../Utils/Logger.h"
6 #include "Renderer.h"
7 #include "../autogen/sprites.h"
8 #include "../config.h"
9 #include "../game.h"
10 #include "../nx.h"
11
12 #include <SDL_image.h>
13 #include <json.hpp>
14 #include <utf8.h>
15 #include <fstream>
16 #include <iostream>
17
18 using namespace NXE::Graphics;
19
20 namespace NXE
21 {
22 namespace Graphics
23 {
24
Font()25 Font::Font()
26 : _height(0)
27 , _base(0)
28 {
29 }
30
load()31 bool Font::load()
32 {
33 cleanup();
34 std::string font = std::string("font_" + std::to_string(Renderer::getInstance()->scale) + ".fnt");
35 LOG_DEBUG("Loading font file {}", font.c_str());
36
37 // special empty glyph
38 _glyphs[0] = Font::Glyph{0, 0, 0, 0, 0, 0, 0, 0, 0};
39
40 std::string path = ResourceManager::getInstance()->getPath(font);
41 if (ResourceManager::getInstance()->fileExists(path))
42 {
43 _upscale = 1;
44 }
45 else
46 {
47 _upscale = Renderer::getInstance()->scale;
48 font = std::string("font_1.fnt");
49 path = ResourceManager::getInstance()->getPath(font);
50 }
51
52 LOG_DEBUG("Loading font file {}", path.c_str());
53
54 std::ifstream fl;
55 fl.open(widen(path), std::ifstream::in | std::ifstream::binary);
56 if (fl.is_open())
57 {
58 nlohmann::json fontdef = nlohmann::json::parse(fl);
59
60 _height = fontdef["common"]["lineHeight"].get<uint32_t>();
61 _base = fontdef["common"]["base"].get<uint32_t>();
62
63 for (auto glyph : fontdef["chars"])
64 {
65 _glyphs[glyph["id"].get<uint32_t>()] = Font::Glyph{
66 glyph["id"].get<uint32_t>(), glyph["page"].get<uint32_t>(), glyph["x"].get<uint32_t>(),
67 glyph["y"].get<uint32_t>(), glyph["width"].get<uint32_t>(), glyph["height"].get<uint32_t>(),
68 glyph["xadvance"].get<uint32_t>(), glyph["xoffset"].get<uint32_t>(), glyph["yoffset"].get<uint32_t>()};
69 }
70
71 for (auto atlas : fontdef["pages"])
72 {
73 std::string atlaspath = ResourceManager::getInstance()->getPath(atlas.get<std::string>());
74 SDL_Surface *surf = IMG_Load(atlaspath.c_str());
75 _atlases.push_back(SDL_CreateTextureFromSurface(Renderer::getInstance()->renderer(), surf));
76 SDL_FreeSurface(surf);
77 }
78 }
79 else
80 {
81 LOG_ERROR("Error opening font file {}", path.c_str());
82 return false;
83 }
84
85 return true;
86 }
87
cleanup()88 void Font::cleanup()
89 {
90 _height = 0;
91 _base = 0;
92 _glyphs.clear();
93 for (auto atlas : _atlases)
94 {
95 SDL_DestroyTexture(atlas);
96 }
97 _atlases.clear();
98 _upscale = 1;
99 }
100
~Font()101 Font::~Font()
102 {
103 cleanup();
104 }
105
glyph(uint32_t codepoint)106 const Font::Glyph &Font::glyph(uint32_t codepoint)
107 {
108 if (_glyphs.find(codepoint) != _glyphs.end())
109 {
110 return _glyphs.at(codepoint);
111 }
112 else
113 {
114 LOG_WARN("No glyph for codepoint {}", codepoint);
115 return _glyphs.at(0);
116 }
117 }
118
atlas(uint32_t idx)119 SDL_Texture *Font::atlas(uint32_t idx)
120 {
121 return _atlases.at(idx);
122 }
123
draw(int x,int y,const std::string & text,uint32_t color,bool isShaded)124 uint32_t Font::draw(int x, int y, const std::string &text, uint32_t color, bool isShaded)
125 {
126 x *= Renderer::getInstance()->scale;
127 y *= Renderer::getInstance()->scale;
128
129 int orgx = x;
130 int i = 0;
131 SDL_Rect dstrect;
132 SDL_Rect shdrect;
133 SDL_Rect srcrect;
134
135 int r, g, b;
136
137 r = ((color >> 16) & 0xFF);
138 g = ((color >> 8) & 0xFF);
139 b = ((color)&0xFF);
140
141 std::string::const_iterator it = (rtl() ? text.end() : text.begin());
142 while (it != (rtl() ? text.begin() : text.end()) )
143 {
144 char32_t ch;
145 if (rtl()) ch = utf8::prior(it, text.begin());
146 else ch = utf8::next(it, text.end());
147
148 Glyph glyph = this->glyph(ch);
149 SDL_Texture *atlas = this->atlas(glyph.atlasid);
150
151 if (ch == '=' && game.mode != GM_CREDITS)
152 {
153 if (_rendering)
154 {
155 int offset = (int)round(((double)_height / (double)Renderer::getInstance()->scale - 6.) / 2.);
156 Renderer::getInstance()->sprites.drawSprite((x / Renderer::getInstance()->scale), (y / Renderer::getInstance()->scale) + offset, SPR_TEXTBULLET);
157 }
158 }
159 else if (_rendering && ch != ' ')
160 {
161 dstrect.x = x + (glyph.xoffset * _upscale);
162 dstrect.y = y + (glyph.yoffset * _upscale);
163 dstrect.w = glyph.w * _upscale;
164 dstrect.h = glyph.h * _upscale;
165
166 srcrect.x = glyph.x;
167 srcrect.y = glyph.y;
168 srcrect.w = glyph.w;
169 srcrect.h = glyph.h;
170
171 if (Renderer::getInstance()->isClipSet())
172 {
173 if (_upscale > 1)
174 Renderer::getInstance()->clip(srcrect, dstrect);
175 else
176 Renderer::getInstance()->clipScaled(srcrect, dstrect);
177 }
178 if (isShaded)
179 {
180 shdrect.x = x + (glyph.xoffset * _upscale);
181 shdrect.y = y + (glyph.yoffset * _upscale + _shadowOffset * Renderer::getInstance()->scale);
182 shdrect.w = glyph.w * _upscale;
183 shdrect.h = glyph.h * _upscale;
184 SDL_SetTextureColorMod(atlas, 0, 0, 0);
185 SDL_RenderCopy(Renderer::getInstance()->renderer(), atlas, &srcrect, &shdrect);
186 SDL_SetTextureColorMod(atlas, 255, 255, 255);
187 }
188 SDL_SetTextureColorMod(atlas, r, g, b);
189 SDL_RenderCopy(Renderer::getInstance()->renderer(), atlas, &srcrect, &dstrect);
190 SDL_SetTextureColorMod(atlas, 255, 255, 255);
191 }
192
193 if (ch == ' ')
194 { // 10.5 px for spaces - make smaller than they really are - the default
195 if (rtl())
196 {
197 x -= (Renderer::getInstance()->scale == 1) ? 5 : 10;
198 if (i & 1)
199 x--;
200 }
201 else
202 {
203 x += (Renderer::getInstance()->scale == 1) ? 5 : 10;
204 if (i & 1)
205 x++;
206 }
207 }
208 else if (ch == '=' && game.mode != GM_CREDITS)
209 {
210 if (rtl()) x -= 7 * Renderer::getInstance()->scale;
211 else x += 7 * Renderer::getInstance()->scale;
212 }
213 else
214 {
215 if (rtl()) x -= glyph.xadvance * _upscale;
216 else x += glyph.xadvance * _upscale;
217 }
218 i++;
219 }
220
221 // return the final width of the text drawn
222 return abs((x - orgx) / Renderer::getInstance()->scale);
223 }
224
drawLTR(int x,int y,const std::string & text,uint32_t color,bool isShaded)225 uint32_t Font::drawLTR(int x, int y, const std::string &text, uint32_t color, bool isShaded)
226 {
227 x *= Renderer::getInstance()->scale;
228 y *= Renderer::getInstance()->scale;
229
230 int orgx = x;
231 int i = 0;
232 SDL_Rect dstrect;
233 SDL_Rect shdrect;
234 SDL_Rect srcrect;
235
236 int r, g, b;
237
238 r = ((color >> 16) & 0xFF);
239 g = ((color >> 8) & 0xFF);
240 b = ((color)&0xFF);
241
242 std::string::const_iterator it = text.begin();
243 while (it != text.end() )
244 {
245 char32_t ch;
246 ch = utf8::next(it, text.end());
247
248 Glyph glyph = this->glyph(ch);
249 SDL_Texture *atlas = this->atlas(glyph.atlasid);
250
251 if (ch == '=' && game.mode != GM_CREDITS)
252 {
253 if (_rendering)
254 {
255 int offset = (int)round(((double)_height / (double)Renderer::getInstance()->scale - 6.) / 2.);
256 Renderer::getInstance()->sprites.drawSprite((x / Renderer::getInstance()->scale), (y / Renderer::getInstance()->scale) + offset, SPR_TEXTBULLET);
257 }
258 }
259 else if (_rendering && ch != ' ')
260 {
261 dstrect.x = x + (glyph.xoffset * _upscale);
262 dstrect.y = y + (glyph.yoffset * _upscale);
263 dstrect.w = glyph.w * _upscale;
264 dstrect.h = glyph.h * _upscale;
265
266 srcrect.x = glyph.x;
267 srcrect.y = glyph.y;
268 srcrect.w = glyph.w;
269 srcrect.h = glyph.h;
270
271 if (Renderer::getInstance()->isClipSet())
272 {
273 if (_upscale > 1)
274 Renderer::getInstance()->clip(srcrect, dstrect);
275 else
276 Renderer::getInstance()->clipScaled(srcrect, dstrect);
277 }
278 if (isShaded)
279 {
280 shdrect.x = x + (glyph.xoffset * _upscale);
281 shdrect.y = y + glyph.yoffset * _upscale + _shadowOffset * Renderer::getInstance()->scale;
282 shdrect.w = glyph.w * _upscale;
283 shdrect.h = glyph.h * _upscale;
284 SDL_SetTextureColorMod(atlas, 0, 0, 0);
285 SDL_RenderCopy(Renderer::getInstance()->renderer(), atlas, &srcrect, &shdrect);
286 SDL_SetTextureColorMod(atlas, 255, 255, 255);
287 }
288 SDL_SetTextureColorMod(atlas, r, g, b);
289 SDL_RenderCopy(Renderer::getInstance()->renderer(), atlas, &srcrect, &dstrect);
290 SDL_SetTextureColorMod(atlas, 255, 255, 255);
291 }
292
293 if (ch == ' ')
294 { // 10.5 px for spaces - make smaller than they really are - the default
295 x += (Renderer::getInstance()->scale == 1) ? 5 : 10;
296 if (i & 1)
297 x++;
298 }
299 else if (ch == '=' && game.mode != GM_CREDITS)
300 {
301 x += 7 * Renderer::getInstance()->scale;
302 }
303 else
304 {
305 x += glyph.xadvance * _upscale;
306 }
307 i++;
308 }
309
310 // return the final width of the text drawn
311 return abs((x - orgx) / Renderer::getInstance()->scale);
312 }
313
getWidth(const std::string & text)314 uint32_t Font::getWidth(const std::string &text)
315 {
316 _rendering = false;
317
318 uint32_t wd = draw(0, 0, text);
319
320 _rendering = true;
321
322 return wd;
323 }
324
getHeight() const325 uint32_t Font::getHeight() const
326 {
327 return _height / ((_upscale == 1) ? Renderer::getInstance()->scale : 1);
328 }
329
getBase() const330 uint32_t Font::getBase() const
331 {
332 return _base / ((_upscale == 1) ? Renderer::getInstance()->scale : 1);
333 }
334
335 }; // namespace Graphics
336 }; // namespace NXE
337