Fix issue in Rocket.lua script.
[Cafu-Engine.git] / CaTools / MakeFont.cpp
blob9e68a73690ec1f9a5a34e506db1d975cdcbb9f79
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
7 #include <iomanip>
8 #include <iostream>
9 #include <fstream>
10 #include <string>
11 #include <limits>
13 #include <ft2build.h>
14 #include FT_FREETYPE_H
16 #include "Bitmap/Bitmap.hpp"
17 #include "ConsoleCommands/Console.hpp"
18 #include "ConsoleCommands/ConsoleStdout.hpp"
19 #include "FileSys/FileManImpl.hpp"
20 #include "Templates/Array.hpp"
22 #if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER<1300)
23 #define vsnprintf _vsnprintf
24 #endif
27 static cf::ConsoleStdoutT ConsoleStdout;
28 cf::ConsoleI* Console=&ConsoleStdout;
30 static cf::FileSys::FileManImplT FileManImpl;
31 cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl;
34 bool DebugPNGs=false;
37 struct GlyphInfoT
39 unsigned long GlyphIndex;
41 float BearingX; ///< The horizontal offset of the bitmap relative to the cursor position.
42 float BearingY; ///< The vertical offset of the bitmap relative to the cursor position (y-axis points up!).
43 float AdvanceX; ///< How much the cursor position should be advanced horizontally for rendering the next character.
45 int Width; ///< The width in pixels of the bitmap of this glyph.
46 int Height; ///< The height in pixels of the bitmap of this glyph.
48 int BitmapNr; ///< The index of the larger bitmap this glyphs bitmap is embedded in.
49 float s1; ///< The s1 tex-coord into the larger bitmap.
50 float t1; ///< The t1 tex-coord into the larger bitmap.
51 float s2; ///< The s2 tex-coord into the larger bitmap.
52 float t2; ///< The t2 tex-coord into the larger bitmap.
56 std::string va(const char* FormatString, ...)
58 va_list ArgList;
59 char Buffer[1024];
61 if (!FormatString) return "";
63 va_start(ArgList, FormatString);
64 vsnprintf(Buffer, 1024-1, FormatString, ArgList);
65 Buffer[1024-1]=0;
66 va_end(ArgList);
68 return Buffer;
72 /// This class "allocates" rectangular areas in larger bitmaps.
73 class RectBitmapAllocatorT
75 public:
77 RectBitmapAllocatorT(uint32_t BitmapInitColor, unsigned long BitmapSizeS=256, unsigned long BitmapSizeT=256)
78 : BITMAP_SIZE_S(BitmapSizeS),
79 BITMAP_SIZE_T(BitmapSizeT),
80 BITMAP_INIT_COLOR(BitmapInitColor)
82 for (unsigned long s=0; s<BITMAP_SIZE_S; s++)
83 BitmapAllocated.PushBack(BITMAP_SIZE_T);
86 ~RectBitmapAllocatorT()
88 for (unsigned long BitmapNr=0; BitmapNr<Bitmaps.Size(); BitmapNr++)
89 delete Bitmaps[BitmapNr];
92 bool Allocate(unsigned long SizeS, unsigned long SizeT, unsigned long& BitmapNr, unsigned long& PosS, unsigned long& PosT)
94 if (SizeS>BITMAP_SIZE_S) return false;
95 if (SizeT>BITMAP_SIZE_T) return false;
97 BitmapNr=Bitmaps.Size()-1;
98 if (AllocateHelper(SizeS, SizeT, PosS, PosT)) return true;
100 BitmapT* Bitmap=new BitmapT(BITMAP_SIZE_S, BITMAP_SIZE_T);
101 for (unsigned long i=0; i<Bitmap->Data.Size(); i++)
102 Bitmap->Data[i]=BITMAP_INIT_COLOR;
103 Bitmaps.PushBack(Bitmap);
105 for (unsigned long s=0; s<BITMAP_SIZE_S; s++)
106 BitmapAllocated[s]=0;
108 BitmapNr=Bitmaps.Size()-1;
109 return AllocateHelper(SizeS, SizeT, PosS, PosT);
113 const unsigned long BITMAP_SIZE_S;
114 const unsigned long BITMAP_SIZE_T;
115 const uint32_t BITMAP_INIT_COLOR;
116 ArrayT<BitmapT*> Bitmaps;
119 private:
121 RectBitmapAllocatorT(const RectBitmapAllocatorT&); ///< Use of the Copy Constructor is not allowed.
122 void operator = (const RectBitmapAllocatorT&); ///< Use of the Assignment Operator is not allowed.
124 bool AllocateHelper(unsigned long SizeS, unsigned long SizeT, unsigned long& PosS, unsigned long& PosT)
126 unsigned long Best=BITMAP_SIZE_T;
128 for (unsigned long s=0; s<=BITMAP_SIZE_S-SizeS; s++)
130 unsigned long Best2=0;
131 unsigned long s2;
133 for (s2=0; s2<SizeS; s2++)
135 if (BitmapAllocated[s+s2]>=Best) break;
136 if (BitmapAllocated[s+s2]>Best2) Best2=BitmapAllocated[s+s2];
139 if (s2==SizeS)
141 // Gültige Position gefunden
142 PosS=s;
143 PosT=Best=Best2;
147 if (Best+SizeT>BITMAP_SIZE_T) return false;
149 for (unsigned long s=0; s<SizeS; s++) BitmapAllocated[PosS+s]=Best+SizeT;
150 return true;
153 ArrayT<unsigned long> BitmapAllocated;
157 void ProcessNewGlyph(FT_Face& Face, GlyphInfoT& Glyph, RectBitmapAllocatorT& RBA)
159 unsigned long PosS=0;
160 unsigned long PosT=0;
162 // IMPORTANT:
163 // Our bitmaps are rendered by the Cafu engine using bilinear filtering.
164 // Therefore, we add a safety margin (a frame) of 1 pixel thickness around each glyph bitmap,
165 // i.e. we increase its width and height by two pixels.
166 // This safety margin is transparently added here in this function -- no user code is supposed to ever become aware of it!
167 unsigned long BitmapNr;
168 if (!RBA.Allocate(Face->glyph->bitmap.width+2, Face->glyph->bitmap.rows+2, BitmapNr, PosS, PosT))
170 std::cout << "WARNING: Unable to process glyph!\n";
172 if (RBA.Bitmaps.Size()==0) return; // That should never happen (but *could*, e.g. if the glpyh bitmap is larger than the bitmaps of the RBA).
173 BitmapNr=0;
174 PosS=0;
175 PosT=0;
178 BitmapT* Bitmap=RBA.Bitmaps[BitmapNr];
180 // Copy the glyphs bitmap into the larger bitmap.
181 // The cast to `unsigned int` is needed because in FreeType, the type of the `rows` and
182 // `width` fields was changed in version 2.5.4 (see its docs/CHANGES file for details)
183 // and some Linux distros still ship with FreeType 2.5.3 or older, whereas others ship
184 // with 2.5.4 or newer.
185 for (unsigned int y = 0; y < (unsigned int)Face->glyph->bitmap.rows; y++)
186 for (unsigned int x = 0; x < (unsigned int)Face->glyph->bitmap.width; x++)
188 const unsigned char GrayValue=Face->glyph->bitmap.buffer[x+y*Face->glyph->bitmap.pitch];
190 if (DebugPNGs) Bitmap->SetPixel(PosS+x+1, PosT+y+1, GrayValue, GrayValue, GrayValue, 255);
191 else Bitmap->SetPixel(PosS+x+1, PosT+y+1, 255, 255, 255, GrayValue);
195 // Fill out the Glyph structure.
196 Glyph.BearingX=float(Face->glyph->bitmap_left-1); // The -1 is required in order to compensate for our "safety frame".
197 Glyph.BearingY=float(Face->glyph->bitmap_top+1); // The +1 is required in order to compensate for our "safety frame". (Note that the bearing is relative to the *UPPER* left corner of the bitmap, not the lower left.)
198 Glyph.AdvanceX=float(Face->glyph->advance.x)/64.0f;
199 // Glyph.AdvanceX=float(Face->glyph->linearHoriAdvance)/65536.0f;
201 Glyph.Width =Face->glyph->bitmap.width+2;
202 Glyph.Height=Face->glyph->bitmap.rows +2;
204 Glyph.BitmapNr=BitmapNr;
205 Glyph.s1=float(PosS)/float(RBA.BITMAP_SIZE_S);
206 Glyph.t1=float(PosT)/float(RBA.BITMAP_SIZE_T);
207 Glyph.s2=float(PosS+Face->glyph->bitmap.width+2)/float(RBA.BITMAP_SIZE_S);
208 Glyph.t2=float(PosT+Face->glyph->bitmap.rows +2)/float(RBA.BITMAP_SIZE_T);
212 int Usage()
214 std::cout << "\nUSAGE: MakeFont FontFileName [OPTIONS]\n";
215 std::cout << "\n";
216 std::cout << "OPTIONS:\n";
217 std::cout << "-m=MaterialBaseName\n";
218 std::cout << " If this is given, a Font.cmat material definition file will be created\n";
219 std::cout << " automatically, using MaterialBaseName as a basis for creating the material\n";
220 std::cout << " names.\n";
221 std::cout << "\n";
222 std::cout << "-debug\n";
223 std::cout << " Creates the FontImage_X_Y.png files with false colors.\n";
224 std::cout << "\n";
225 std::cout << "Example:\n";
226 std::cout << " MakeFont c:\\WINNT\\Fonts\\arial.ttf -m=Arial\n";
227 return 1;
231 int main(int ArgC, const char* ArgV[])
233 // Output header.
234 std::cout << "The Cafu Font Maker, version " << __DATE__ << ".\n\n";
235 std::cout << "Portions of this software are copyright (c) 2006 The FreeType Project\n(www.freetype.org). All rights reserved.\n\n";
237 // Initialize the FileMan by mounting the default file system.
238 // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of
239 // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application.
240 cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", "");
241 // cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE");
242 // cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE");
244 // Parse the command-line arguments.
245 std::string FontFileName="";
246 std::string MaterialBaseName="";
248 for (int ArgNr=1; ArgNr<ArgC; ArgNr++)
250 if (strncmp(ArgV[ArgNr], "-m=" , 3)==0) MaterialBaseName=ArgV[ArgNr]+3;
251 else if (strcmp (ArgV[ArgNr], "-debug" )==0) DebugPNGs=true;
252 else if (ArgV[ArgNr][0]=='-') return Usage();
253 else if (FontFileName=="") FontFileName=ArgV[ArgNr];
254 else return Usage();
257 if (FontFileName=="") return Usage();
260 // Init the FreeType library.
261 FT_Library ftLib;
262 if (FT_Init_FreeType(&ftLib)!=0)
264 std::cout << "Error: Could not init the FreeType library.\n";
265 return 1;
269 // Load the font face.
270 FT_Face ftFontFace;
271 if (FT_New_Face(ftLib, FontFileName.c_str(), 0, &ftFontFace)!=0)
273 std::cout << "Error: Could not load the font face from \"" << FontFileName << "\".\n";
274 return 1;
278 const int PointSizes[]= { 12, 24, 48 }; // The sizes in points (1/72th of an inch).
280 for (unsigned long SizeNr=0; SizeNr<3; SizeNr++)
282 const int SizeInPoints=PointSizes[SizeNr];
284 // Set the character size to SizeInPoints points at 72 DPI, which implies that the size in pixels is the same as SizeInPoints.
285 if (FT_Set_Char_Size(ftFontFace, SizeInPoints*64, 0, 72, 72)!=0)
287 std::cout << "Error: Could not set the character size to " << SizeInPoints << "pt.\n";
288 continue;
291 RectBitmapAllocatorT RBA(DebugPNGs ? 0xFFFF00FF : 0x00FFFFFF); // The default color is "invisible white", that is, RGB=1 and A=0.
292 ArrayT<unsigned long> CharToGlyphInfoNr;
293 ArrayT<GlyphInfoT> GlyphInfos;
295 for (unsigned long CharNr=0; CharNr<256; CharNr++)
297 // const unsigned long GlyphIndex=(CharNr<128) ? FT_Get_Char_Index(ftFontFace, CharNr) : 0;
298 const unsigned long GlyphIndex=FT_Get_Char_Index(ftFontFace, CharNr);
300 // Determine if we already have a GlyphInfoT object with that GlyphIndex, that is, determine whether GlyphIndex has occurred before.
301 unsigned long GlyphInfoNr;
303 for (GlyphInfoNr=0; GlyphInfoNr<GlyphInfos.Size(); GlyphInfoNr++)
304 if (GlyphInfos[GlyphInfoNr].GlyphIndex==GlyphIndex)
305 break;
307 CharToGlyphInfoNr.PushBack(GlyphInfoNr);
309 // If we already have such a GlyphInfoT, we're done with this character.
310 if (GlyphInfoNr<GlyphInfos.Size()) continue;
312 // Load the glyph at GlyphIndex into the glyph slot of ftFontFace (i.e. ftFontFace->glyph).
313 if (FT_Load_Glyph(ftFontFace, GlyphIndex, FT_LOAD_RENDER)!=0)
315 std::cout << "Error: Could not obtain the glyph at index " << GlyphIndex << ".\n";
316 continue; // TODO: Is this proper error handling???
320 // Insert and store the new glyph info.
321 GlyphInfos.PushBackEmpty();
322 GlyphInfos[GlyphInfos.Size()-1].GlyphIndex=GlyphIndex;
324 ProcessNewGlyph(ftFontFace, GlyphInfos[GlyphInfos.Size()-1], RBA);
328 // Save all resulting texture maps to disk.
329 for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++)
330 RBA.Bitmaps[TexMapNr]->SaveToDisk(va("FontImage_%i_%lu.png", SizeInPoints, TexMapNr).c_str());
333 // Save the resulting font description to disk.
334 std::ofstream FontFile(va("FontDescr_%i.cfont", SizeInPoints).c_str(), std::ios::out);
335 // if (FontFile.bad()) ...
337 const int sigdigits=std::numeric_limits<float>::digits10;
338 FontFile << std::setprecision(sigdigits);
340 FontFile << "// Values that are not per-glyph, but global to the font face at the current scale (" << SizeInPoints << " pixels).\n";
341 FontFile << "// The values are: ascender, descender and height.\n";
342 FontFile << "global " << float(ftFontFace->size->metrics.ascender )/64.0f << " "
343 << float(ftFontFace->size->metrics.descender)/64.0f << " "
344 << float(ftFontFace->size->metrics.height )/64.0f << "\n";
345 FontFile << "\n";
347 FontFile << "// For each of the " << CharToGlyphInfoNr.Size() << " characters, record the index into the glyph infos below.\n";
348 for (unsigned long CharNr=0; CharNr<CharToGlyphInfoNr.Size(); CharNr++)
350 if (CharNr>0)
352 if ((CharNr % 16)==0) FontFile << "\n"; else FontFile << " ";
355 FontFile << CharToGlyphInfoNr[CharNr];
357 FontFile << "\n\n";
359 FontFile << "// The glyphs below refer to larger bitmaps, represented by the following materials.\n";
360 for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++)
361 FontFile << "matname Fonts/" << MaterialBaseName << "_" << SizeInPoints << "_" << TexMapNr << "\n";
362 FontFile << "\n";
364 FontFile << "// The glyphs (num, bearing x, bearing y, advance x, width, height, bitmap num, x1, y1, x2, y2).\n";
365 for (unsigned long GINr=0; GINr<GlyphInfos.Size(); GINr++)
367 const GlyphInfoT& Glyph=GlyphInfos[GINr];
369 FontFile << "glyph " << GINr
370 << " " << Glyph.BearingX
371 << " " << Glyph.BearingY
372 << " " << Glyph.AdvanceX
373 << " " << Glyph.Width
374 << " " << Glyph.Height
375 << " " << Glyph.BitmapNr
376 << " " << Glyph.s1
377 << " " << Glyph.t1
378 << " " << Glyph.s2
379 << " " << Glyph.t2
380 << "\n";
382 FontFile << "\n";
384 FontFile << "// The kerning table.\n";
385 for (unsigned long GI1Nr=0; GI1Nr<GlyphInfos.Size(); GI1Nr++)
386 for (unsigned long GI2Nr=0; GI2Nr<GlyphInfos.Size(); GI2Nr++)
388 const GlyphInfoT& gi1=GlyphInfos[GI1Nr];
389 const GlyphInfoT& gi2=GlyphInfos[GI2Nr];
391 FT_Vector KerningVec;
393 if (FT_Get_Kerning(ftFontFace, gi1.GlyphIndex, gi2.GlyphIndex, FT_KERNING_DEFAULT /*FT_KERNING_UNFITTED*/, &KerningVec)!=0) continue;
394 if (KerningVec.x==0) continue;
396 const float HorKerning=float(KerningVec.x)/64.0f;
398 FontFile << "k " << GI1Nr << " " << GI2Nr << " " << HorKerning << "\n";
402 // Auto-create a materials definition file if MaterialBaseName was given.
403 if (MaterialBaseName!="")
405 std::ofstream CMatFile("Font.cmat", SizeNr==0 ? std::ios::out : std::ios::out | std::ios::app);
407 for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++)
409 CMatFile << "Fonts/" << MaterialBaseName << "_" << SizeInPoints << "_" << TexMapNr << "\n"; // Fonts/Arial_24_0
410 CMatFile << "{\n";
411 CMatFile << " diffusemap " << "FontImage" /*MaterialBaseName*/ << "_" << SizeInPoints << "_" << TexMapNr << ".png, minFilter linear, noScaleDown\n";
412 CMatFile << "\n";
413 CMatFile << " noDynLight // Should be replaced e.g. by \"LightShader none\"...\n";
414 CMatFile << "\n";
415 CMatFile << " blendFunc src_alpha one_minus_src_alpha\n";
416 CMatFile << " red ambientLightRed // Hmmm. Maybe we should rather use fParam0...fParam2 here.\n";
417 CMatFile << " green ambientLightGreen\n";
418 CMatFile << " blue ambientLightBlue\n";
419 CMatFile << " ambientMask d // Don't write into the depth buffer!\n";
420 CMatFile << "}\n";
421 CMatFile << "\n";
427 // Clean up and quit.
428 FT_Done_Face(ftFontFace);
429 FT_Done_FreeType(ftLib);
430 return 0;