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.
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
27 static cf::ConsoleStdoutT ConsoleStdout
;
28 cf::ConsoleI
* Console
=&ConsoleStdout
;
30 static cf::FileSys::FileManImplT FileManImpl
;
31 cf::FileSys::FileManI
* cf::FileSys::FileMan
=&FileManImpl
;
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
, ...)
61 if (!FormatString
) return "";
63 va_start(ArgList
, FormatString
);
64 vsnprintf(Buffer
, 1024-1, FormatString
, ArgList
);
72 /// This class "allocates" rectangular areas in larger bitmaps.
73 class RectBitmapAllocatorT
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
;
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;
133 for (s2
=0; s2
<SizeS
; s2
++)
135 if (BitmapAllocated
[s
+s2
]>=Best
) break;
136 if (BitmapAllocated
[s
+s2
]>Best2
) Best2
=BitmapAllocated
[s
+s2
];
141 // Gültige Position gefunden
147 if (Best
+SizeT
>BITMAP_SIZE_T
) return false;
149 for (unsigned long s
=0; s
<SizeS
; s
++) BitmapAllocated
[PosS
+s
]=Best
+SizeT
;
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;
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).
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
);
214 std::cout
<< "\nUSAGE: MakeFont FontFileName [OPTIONS]\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";
222 std::cout
<< "-debug\n";
223 std::cout
<< " Creates the FontImage_X_Y.png files with false colors.\n";
225 std::cout
<< "Example:\n";
226 std::cout
<< " MakeFont c:\\WINNT\\Fonts\\arial.ttf -m=Arial\n";
231 int main(int ArgC
, const char* ArgV
[])
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
];
257 if (FontFileName
=="") return Usage();
260 // Init the FreeType library.
262 if (FT_Init_FreeType(&ftLib
)!=0)
264 std::cout
<< "Error: Could not init the FreeType library.\n";
269 // Load the font face.
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";
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";
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
)
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";
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
++)
352 if ((CharNr
% 16)==0) FontFile
<< "\n"; else FontFile
<< " ";
355 FontFile
<< CharToGlyphInfoNr
[CharNr
];
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";
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
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
411 CMatFile
<< " diffusemap " << "FontImage" /*MaterialBaseName*/ << "_" << SizeInPoints
<< "_" << TexMapNr
<< ".png, minFilter linear, noScaleDown\n";
413 CMatFile
<< " noDynLight // Should be replaced e.g. by \"LightShader none\"...\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";
427 // Clean up and quit.
428 FT_Done_Face(ftFontFace
);
429 FT_Done_FreeType(ftLib
);