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