1 /*
2  * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
3  *
4  * Solarus is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Solarus is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "solarus/core/CurrentQuest.h"
18 #include "solarus/core/Debug.h"
19 #include "solarus/core/FontResource.h"
20 #include "solarus/core/QuestFiles.h"
21 #include "solarus/graphics/Surface.h"
22 #include <utility>
23 
24 namespace Solarus {
25 
26 bool FontResource::fonts_loaded = false;
27 std::map<std::string, FontResource::FontFile> FontResource::fonts;
28 
29 /**
30  * \brief Initializes the font system.
31  */
initialize()32 void FontResource::initialize() {
33 
34   TTF_Init();
35 }
36 
37 /**
38  * \brief Closes the font system.
39  */
quit()40 void FontResource::quit() {
41 
42   fonts.clear();
43   fonts_loaded = false;
44   TTF_Quit();
45 }
46 
47 /**
48  * \brief Loads the fonts declared in the quest resource list.
49  */
load_fonts()50 void FontResource::load_fonts() {
51 
52   // Get the list of available fonts.
53   const std::map<std::string, std::string>& font_resource =
54       CurrentQuest::get_resources(ResourceType::FONT);
55 
56   for (const auto& kvp: font_resource) {
57 
58     const std::string& font_id = kvp.first;
59 
60     FontFile font;
61 
62     // Load the font.
63 
64     bool bitmap_font = false;
65     const std::string file_name_start = std::string("fonts/") + font_id;
66     if (QuestFiles::data_file_exists(file_name_start + ".png")) {
67       font.file_name = file_name_start + ".png";
68       bitmap_font = true;
69     }
70     else if (QuestFiles::data_file_exists(file_name_start + ".PNG")) {
71       font.file_name = file_name_start + ".PNG";
72       bitmap_font = true;
73     }
74     else if (QuestFiles::data_file_exists(file_name_start + ".ttf")) {
75       font.file_name = file_name_start + ".ttf";
76     }
77     else if (QuestFiles::data_file_exists(file_name_start + ".TTF")) {
78       font.file_name = file_name_start + ".TTF";
79     }
80     else if (QuestFiles::data_file_exists(file_name_start + ".otf")) {
81       font.file_name = file_name_start + ".otf";
82     }
83     else if (QuestFiles::data_file_exists(file_name_start + ".OTF")) {
84       font.file_name = file_name_start + ".OTF";
85     }
86     else if (QuestFiles::data_file_exists(file_name_start + ".ttc")) {
87       font.file_name = file_name_start + ".ttc";
88     }
89     else if (QuestFiles::data_file_exists(file_name_start + ".TTC")) {
90       font.file_name = file_name_start + ".TTC";
91     }
92     else if (QuestFiles::data_file_exists(file_name_start + ".fon")) {
93       font.file_name = file_name_start + ".fon";
94     }
95     else if (QuestFiles::data_file_exists(file_name_start + ".FON")) {
96       font.file_name = file_name_start + ".FON";
97     }
98     else {
99       Debug::error(std::string("Cannot find font file 'fonts/")
100           + font_id + "' (tried with extensions .png, .ttf, .otf, .ttc and .fon)"
101       );
102       continue;
103     }
104 
105     if (bitmap_font) {
106       // It's a bitmap font.
107       font.bitmap_font = Surface::create(font.file_name, Surface::DIR_DATA);
108     }
109 
110     else {
111       // It's an outline font.
112       font.buffer = QuestFiles::data_file_read(font.file_name);
113       font.bitmap_font = nullptr;
114     }
115 
116     fonts.emplace(font_id, std::move(font));
117   }
118 
119   fonts_loaded = true;
120 }
121 
122 /**
123  * \brief Returns the id of default font.
124  * \return Id of the first font in alphabetical order, or an empty string
125  * if there is no font at all.
126  */
get_default_font_id()127 std::string FontResource::get_default_font_id() {
128 
129   if (!fonts_loaded) {
130     load_fonts();
131   }
132 
133   if (fonts.empty()) {
134     return "";
135   }
136 
137   return fonts.begin()->first;
138 }
139 
140 /**
141  * \brief Returns whether the specified font exists.
142  * \param font_id The id to test.
143  * \return \c true if there is a valid font with this id in the quest resource
144  * list.
145  */
exists(const std::string & font_id)146 bool FontResource::exists(const std::string& font_id) {
147 
148   if (!fonts_loaded) {
149     load_fonts();
150   }
151 
152   return fonts.find(font_id) != fonts.end();
153 }
154 
155 /**
156  * \brief Returns whether the specified font is a bitmap or an outline font.
157  * \param font_id Id of the font to test. It must exist.
158  * \return \c true if this is a bitmap font, \c false if this is an outline font.
159  */
is_bitmap_font(const std::string & font_id)160 bool FontResource::is_bitmap_font(const std::string& font_id) {
161 
162   if (!fonts_loaded) {
163     load_fonts();
164   }
165 
166   const auto& kvp = fonts.find(font_id);
167   Debug::check_assertion(kvp != fonts.end(), std::string("No such font: '") + font_id + "'");
168   return kvp->second.bitmap_font != nullptr;
169 }
170 
171 /**
172  * \brief Returns the surface image of a bitmap font.
173  * \param font_id Id of the bitmap font to get. It must exist.
174  * \return The bitmap.
175  */
get_bitmap_font(const std::string & font_id)176 SurfacePtr FontResource::get_bitmap_font(const std::string& font_id) {
177 
178   if (!fonts_loaded) {
179     load_fonts();
180   }
181 
182   const auto& kvp = fonts.find(font_id);
183   Debug::check_assertion(kvp != fonts.end(), std::string("No such font: '") + font_id + "'");
184   Debug::check_assertion(kvp->second.bitmap_font != nullptr, std::string("This is not a bitmap font: '") + font_id + "'");
185   return kvp->second.bitmap_font;
186 }
187 
188 /**
189  * \brief Returns an outline font with the specified size.
190  * \param font_id Id of the outline font to get. It must exist.
191  * \param size Size to use.
192  * \return The font.
193  */
get_outline_font(const std::string & font_id,int size)194 TTF_Font& FontResource::get_outline_font(const std::string& font_id, int size) {
195 
196   if (!fonts_loaded) {
197     load_fonts();
198   }
199 
200   const auto& kvp = fonts.find(font_id);
201   Debug::check_assertion(kvp != fonts.end(), std::string("No such font: '") + font_id + "'");
202   FontFile& font = kvp->second;
203   Debug::check_assertion(font.bitmap_font == nullptr, std::string("This is not an outline font: '") + font_id + "'");
204 
205   std::map<int, OutlineFontReader>& outline_fonts = kvp->second.outline_fonts;
206 
207   const auto& kvp2 = outline_fonts.find(size);
208   if (kvp2 != outline_fonts.end()) {
209     return *kvp2->second.outline_font;
210   }
211 
212   // First time we want this font with this particular size.
213   SDL_RWops_UniquePtr rw = SDL_RWops_UniquePtr(SDL_RWFromMem(
214       const_cast<char*>(font.buffer.data()),
215       (int) font.buffer.size()
216   ));
217   TTF_Font_UniquePtr outline_font(TTF_OpenFontRW(rw.get(), 0, size));
218   Debug::check_assertion(outline_font != nullptr,
219       std::string("Cannot load font from file '") + font.file_name
220       + "': " + TTF_GetError()
221   );
222   OutlineFontReader reader = { std::move(rw), std::move(outline_font) };
223   outline_fonts.emplace(size, std::move(reader));
224   return *outline_fonts.at(size).outline_font;
225 }
226 
227 }
228 
229