FontLoader.cpp
1
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
26// Headers
28#include <SFML/Graphics/FontLoader.hpp>
29#include <SFML/Graphics/Color.hpp>
30#include <SFML/Graphics/Font.hpp>
31#include <SFML/Graphics/Image.hpp>
32#include <SFML/Graphics/GraphicsContext.hpp>
33#include FT_GLYPH_H
34#include <iostream>
35#include <map>
36#include <vector>
37#include <math.h>
38
39
40namespace
41{
43 // Functor to sort glyphs by size
45 struct SizeCompare
46 {
47 bool operator ()(FT_BitmapGlyph Glyph1, FT_BitmapGlyph Glyph2) const
48 {
49 return Glyph2->bitmap.rows < Glyph1->bitmap.rows;
50 }
51 };
52}
53
54namespace sf
55{
56namespace priv
57{
61FontLoader& FontLoader::GetInstance()
62{
63 static FontLoader Instance;
64
65 return Instance;
66}
67
68
72FontLoader::FontLoader()
73{
74 // Initialize FreeType library
75 FT_Error Error = FT_Init_FreeType(&myLibrary);
76 if (Error)
77 {
78 std::cerr << "Failed to initialize FreeType library (error code : " << Error << ")" << std::endl;
79 return;
80 }
81}
82
83
87FontLoader::~FontLoader()
88{
89 // Shutdown FreeType library
90 if (myLibrary)
91 FT_Done_FreeType(myLibrary);
92}
93
94
98bool FontLoader::LoadFontFromFile(const std::string& Filename, unsigned int CharSize, const Unicode::UTF32String& Charset, Font& LoadedFont)
99{
100 // Check if Freetype is correctly initialized
101 if (!myLibrary)
102 {
103 std::cerr << "Failed to load font \"" << Filename << "\", FreeType has not been initialized" << std::endl;
104 return false;
105 }
106
107 // Create a new font face from the specified file
108 FT_Face FontFace;
109 FT_Error Error = FT_New_Face(myLibrary, Filename.c_str(), 0, &FontFace);
110 if (Error)
111 {
112 std::cerr << "Failed to load font \"" << Filename << "\" (" << GetErrorDesc(Error) << ")" << std::endl;
113 return false;
114 }
115
116 // Create the bitmap font
117 Error = CreateBitmapFont(FontFace, CharSize, Charset, LoadedFont);
118 if (Error)
119 std::cerr << "Failed to load font \"" << Filename << "\" (" << GetErrorDesc(Error) << ")" << std::endl;
120
121 // Delete the font
122 FT_Done_Face(FontFace);
123
124 return Error == 0;
125}
126
127
131bool FontLoader::LoadFontFromMemory(const char* Data, std::size_t SizeInBytes, unsigned int CharSize, const Unicode::UTF32String& Charset, Font& LoadedFont)
132{
133 // Check if Freetype is correctly initialized
134 if (!myLibrary)
135 {
136 std::cerr << "Failed to load font from memory, FreeType has not been initialized" << std::endl;
137 return false;
138 }
139
140 // Create a new font face from the specified memory data
141 FT_Face FontFace;
142 FT_Error Error = FT_New_Memory_Face(myLibrary, reinterpret_cast<const FT_Byte*>(Data), static_cast<FT_Long>(SizeInBytes), 0, &FontFace);
143 if (Error)
144 {
145 std::cerr << "Failed to load font from memory (" << GetErrorDesc(Error) << ")" << std::endl;
146 return false;
147 }
148
149 // Create the bitmap font
150 Error = CreateBitmapFont(FontFace, CharSize, Charset, LoadedFont);
151 if (Error)
152 std::cerr << "Failed to load font from memory (" << GetErrorDesc(Error) << ")" << std::endl;
153
154 // Delete the font
155 FT_Done_Face(FontFace);
156
157 return Error == 0;
158}
159
160
164FT_Error FontLoader::CreateBitmapFont(FT_Face FontFace, unsigned int CharSize, const Unicode::UTF32String& Charset, Font& LoadedFont)
165{
166 // Make sure we have a valid context
167 priv::GraphicsContext Ctx;
168
169 // Let's find how many characters to put in each row to make them fit into a squared texture
170 GLint MaxSize;
171 GLCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxSize));
172 int NbChars = static_cast<int>(sqrt(static_cast<double>(Charset.length())) * 0.75);
173
174 // Clamp the character size to make sure we won't create a texture too big
175 if (NbChars * CharSize >= static_cast<unsigned int>(MaxSize))
176 CharSize = MaxSize / NbChars;
177
178 // Initialize the dimensions
179 unsigned int Left = 0;
180 unsigned int Top = 0;
181 unsigned int TexWidth = Image::GetValidTextureSize(CharSize * NbChars);
182 unsigned int TexHeight = CharSize * NbChars;
183 std::vector<unsigned int> Tops(TexWidth, 0);
184
185 // Create a pixel buffer for rendering every glyph
186 std::vector<Uint8> GlyphsBuffer(TexWidth * TexHeight * 4);
187
188 // Setup the font size
189 FT_Error Error = FT_Set_Pixel_Sizes(FontFace, CharSize, CharSize);
190 if (Error)
191 return Error;
192
193 // Select the unicode character map
194 Error = FT_Select_Charmap(FontFace, FT_ENCODING_UNICODE);
195 if (Error)
196 return Error;
197
198 // Render all glyphs and sort them by size to optimize texture space
199 typedef std::multimap<FT_BitmapGlyph, Uint32, SizeCompare> GlyphTable;
200 GlyphTable Glyphs;
201 for (std::size_t i = 0; i < Charset.length(); ++i)
202 {
203 // Load the glyph corresponding to the current character
204 Error = FT_Load_Char(FontFace, Charset[i], FT_LOAD_TARGET_NORMAL);
205 if (Error)
206 return Error;
207
208 // Convert the glyph to a bitmap (ie. rasterize it)
209 FT_Glyph Glyph;
210 Error = FT_Get_Glyph(FontFace->glyph, &Glyph);
211 if (Error)
212 return Error;
213 FT_Glyph_To_Bitmap(&Glyph, FT_RENDER_MODE_NORMAL, 0, 1);
214 FT_BitmapGlyph BitmapGlyph = (FT_BitmapGlyph)Glyph;
215
216 // Add it to the sorted table of glyphs
217 Glyphs.insert(std::make_pair(BitmapGlyph, Charset[i]));
218 }
219
220 // Copy the rendered glyphs into the texture
221 unsigned int MaxHeight = 0;
222 std::map<Uint32, IntRect> Coords;
223 for (GlyphTable::const_iterator i = Glyphs.begin(); i != Glyphs.end(); ++i)
224 {
225 // Get the bitmap of the current glyph
226 Glyph& CurGlyph = LoadedFont.myGlyphs[i->second];
227 FT_BitmapGlyph BitmapGlyph = i->first;
228 FT_Bitmap& Bitmap = BitmapGlyph->bitmap;
229
230 // Make sure we don't go over the texture width
231 if (Left + Bitmap.width + 1 >= TexWidth)
232 Left = 0;
233
234 // Compute the top coordinate
235 Top = Tops[Left];
236 for (int x = 0; x < Bitmap.width + 1; ++x)
237 Top = std::max(Top, Tops[Left + x]);
238 Top++;
239
240 // Make sure we don't go over the texture height -- resize it if we need more space
241 if (Top + Bitmap.rows + 1 >= TexHeight)
242 {
243 TexHeight *= 2;
244 GlyphsBuffer.resize(TexWidth * TexHeight * 4);
245 }
246
247 // Store the character's position and size
248 CurGlyph.Rectangle.Left = BitmapGlyph->left;
249 CurGlyph.Rectangle.Top = -BitmapGlyph->top;
250 CurGlyph.Rectangle.Right = CurGlyph.Rectangle.Left + Bitmap.width;
251 CurGlyph.Rectangle.Bottom = Bitmap.rows - BitmapGlyph->top;
252 CurGlyph.Advance = BitmapGlyph->root.advance.x >> 16;
253
254 // Texture size may change, so let the texture coordinates be calculated later
255 Coords[i->second] = IntRect(Left + 1, Top + 1, Left + Bitmap.width + 1, Top + Bitmap.rows + 1);
256
257 // Draw the glyph into our bitmap font
258 const Uint8* Pixels = Bitmap.buffer;
259 for (int y = 0; y < Bitmap.rows; ++y)
260 {
261 for (int x = 0; x < Bitmap.width; ++x)
262 {
263 std::size_t Index = x + Left + 1 + (y + Top + 1) * TexWidth;
264 GlyphsBuffer[Index * 4 + 0] = 255;
265 GlyphsBuffer[Index * 4 + 1] = 255;
266 GlyphsBuffer[Index * 4 + 2] = 255;
267 GlyphsBuffer[Index * 4 + 3] = Pixels[x];
268 }
269 Pixels += Bitmap.pitch;
270 }
271
272 // Update the rendering coordinates
273 for (int x = 0; x < Bitmap.width + 1; ++x)
274 Tops[Left + x] = Top + Bitmap.rows;
275 Left += Bitmap.width + 1;
276 if (Top + Bitmap.rows > MaxHeight)
277 MaxHeight = Top + Bitmap.rows;
278
279 // Delete the glyph
280 FT_Done_Glyph((FT_Glyph)BitmapGlyph);
281 }
282
283 // Create the font's texture
284 TexHeight = MaxHeight + 1;
285 GlyphsBuffer.resize(TexWidth * TexHeight * 4);
286 LoadedFont.myTexture.LoadFromPixels(TexWidth, TexHeight, &GlyphsBuffer[0]);
287
288 // Now that the texture is created, we can precompute texture coordinates
289 for (std::size_t i = 0; i < Charset.size(); ++i)
290 {
291 Uint32 CurChar = Charset[i];
292 LoadedFont.myGlyphs[CurChar].TexCoords = LoadedFont.myTexture.GetTexCoords(Coords[CurChar]);
293 }
294
295 // Update the character size (it may have been changed by the function)
296 LoadedFont.myCharSize = CharSize;
297
298 return 0;
299}
300
301
305std::string FontLoader::GetErrorDesc(FT_Error Error)
306{
307 switch (Error)
308 {
309 // Generic errors
310 case FT_Err_Cannot_Open_Resource : return "cannot open resource";
311 case FT_Err_Unknown_File_Format : return "unknown file format";
312 case FT_Err_Invalid_File_Format : return "broken file";
313 case FT_Err_Invalid_Version : return "invalid FreeType version";
314 case FT_Err_Lower_Module_Version : return "module version is too low";
315 case FT_Err_Invalid_Argument : return "invalid argument";
316 case FT_Err_Unimplemented_Feature : return "unimplemented feature";
317 case FT_Err_Invalid_Table : return "broken table";
318 case FT_Err_Invalid_Offset : return "broken offset within table";
319
320 // Glyph / character errors
321 case FT_Err_Invalid_Glyph_Index : return "invalid glyph index";
322 case FT_Err_Invalid_Character_Code : return "invalid character code";
323 case FT_Err_Invalid_Glyph_Format : return "unsupported glyph image format";
324 case FT_Err_Cannot_Render_Glyph : return "cannot render this glyph format";
325 case FT_Err_Invalid_Outline : return "invalid outline";
326 case FT_Err_Invalid_Composite : return "invalid composite glyph";
327 case FT_Err_Too_Many_Hints : return "too many hints";
328 case FT_Err_Invalid_Pixel_Size : return "invalid pixel size";
329
330 // Handle errors
331 case FT_Err_Invalid_Handle : return "invalid object handle";
332 case FT_Err_Invalid_Library_Handle : return "invalid library handle";
333 case FT_Err_Invalid_Driver_Handle : return "invalid module handle";
334 case FT_Err_Invalid_Face_Handle : return "invalid face handle";
335 case FT_Err_Invalid_Size_Handle : return "invalid size handle";
336 case FT_Err_Invalid_Slot_Handle : return "invalid glyph slot handle";
337 case FT_Err_Invalid_CharMap_Handle : return "invalid charmap handle";
338 case FT_Err_Invalid_Cache_Handle : return "invalid cache manager handle";
339 case FT_Err_Invalid_Stream_Handle : return "invalid stream handle";
340
341 // Driver errors
342 case FT_Err_Too_Many_Drivers : return "too many modules";
343 case FT_Err_Too_Many_Extensions : return "too many extensions";
344
345 // Memory errors
346 case FT_Err_Out_Of_Memory : return "out of memory";
347 case FT_Err_Unlisted_Object : return "unlisted object";
348
349 // Stream errors
350 case FT_Err_Cannot_Open_Stream : return "cannot open stream";
351 case FT_Err_Invalid_Stream_Seek : return "invalid stream seek";
352 case FT_Err_Invalid_Stream_Skip : return "invalid stream skip";
353 case FT_Err_Invalid_Stream_Read : return "invalid stream read";
354 case FT_Err_Invalid_Stream_Operation : return "invalid stream operation";
355 case FT_Err_Invalid_Frame_Operation : return "invalid frame operation";
356 case FT_Err_Nested_Frame_Access : return "nested frame access";
357 case FT_Err_Invalid_Frame_Read : return "invalid frame read";
358
359 // Raster errors
360 case FT_Err_Raster_Uninitialized : return "raster uninitialized";
361 case FT_Err_Raster_Corrupted : return "raster corrupted";
362 case FT_Err_Raster_Overflow : return "raster overflow";
363 case FT_Err_Raster_Negative_Height : return "negative height while rastering";
364
365 // Cache errors
366 case FT_Err_Too_Many_Caches : return "too many registered caches";
367
368 // TrueType and SFNT errors
369 case FT_Err_Invalid_Opcode : return "invalid opcode";
370 case FT_Err_Too_Few_Arguments : return "too few arguments";
371 case FT_Err_Stack_Overflow : return "stack overflow";
372 case FT_Err_Code_Overflow : return "code overflow";
373 case FT_Err_Bad_Argument : return "bad argument";
374 case FT_Err_Divide_By_Zero : return "division by zero";
375 case FT_Err_Invalid_Reference : return "invalid reference";
376 case FT_Err_Debug_OpCode : return "found debug opcode";
377 case FT_Err_ENDF_In_Exec_Stream : return "found ENDF opcode in execution stream";
378 case FT_Err_Nested_DEFS : return "nested DEFS";
379 case FT_Err_Invalid_CodeRange : return "invalid code range";
380 case FT_Err_Execution_Too_Long : return "execution context too long";
381 case FT_Err_Too_Many_Function_Defs : return "too many function definitions";
382 case FT_Err_Too_Many_Instruction_Defs : return "too many instruction definitions";
383 case FT_Err_Table_Missing : return "SFNT font table missing";
384 case FT_Err_Horiz_Header_Missing : return "horizontal header (hhea) table missing";
385 case FT_Err_Locations_Missing : return "locations (loca) table missing";
386 case FT_Err_Name_Table_Missing : return "name table missing";
387 case FT_Err_CMap_Table_Missing : return "character map (cmap) table missing";
388 case FT_Err_Hmtx_Table_Missing : return "horizontal metrics (hmtx) table missing";
389 case FT_Err_Post_Table_Missing : return "PostScript (post) table missing";
390 case FT_Err_Invalid_Horiz_Metrics : return "invalid horizontal metrics";
391 case FT_Err_Invalid_CharMap_Format : return "invalid character map (cmap) format";
392 case FT_Err_Invalid_PPem : return "invalid ppem value";
393 case FT_Err_Invalid_Vert_Metrics : return "invalid vertical metrics";
394 case FT_Err_Could_Not_Find_Context : return "could not find context";
395 case FT_Err_Invalid_Post_Table_Format : return "invalid PostScript (post) table format";
396 case FT_Err_Invalid_Post_Table : return "invalid PostScript (post) table";
397
398 // CCF, CID and Type 1 errors
399 case FT_Err_Syntax_Error : return "opcode syntax error";
400 case FT_Err_Stack_Underflow : return "argument stack underflow";
401 case FT_Err_Ignore : return "ignore";
402
403 // BDF errors
404 case FT_Err_Missing_Startfont_Field : return "`STARTFONT' field missing";
405 case FT_Err_Missing_Font_Field : return "`FONT' field missing";
406 case FT_Err_Missing_Size_Field : return "`SIZE' field missing";
407 case FT_Err_Missing_Chars_Field : return "`CHARS' field missing";
408 case FT_Err_Missing_Startchar_Field : return "`STARTCHAR' field missing";
409 case FT_Err_Missing_Encoding_Field : return "`ENCODING' field missing";
410 case FT_Err_Missing_Bbx_Field : return "`BBX' field missing";
411 }
412
413 return "unknown error";
414}
415
416} // namespace priv
417
418} // namespace sf
419
static unsigned int GetValidTextureSize(unsigned int Size)
Get a valid texture size according to hardware support.
Definition Image.cpp:560
@ Left
Left arrow.
Definition Event.hpp:114