root/cafu/trunk/CaTools/MakeFont.cpp

Revision 455, 17.5 KB (checked in by Carsten, 4 months ago)

Updated copyright banners (in C++ files).

Line 
1/*
2=================================================================================
3This file is part of Cafu, the open-source game engine and graphics engine
4for multiplayer, cross-platform, real-time 3D action.
5Copyright (C) 2002-2012 Carsten Fuchs Software.
6
7Cafu is free software: you can redistribute it and/or modify it under the terms
8of the GNU General Public License as published by the Free Software Foundation,
9either version 3 of the License, or (at your option) any later version.
10
11Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with Cafu. If not, see <http://www.gnu.org/licenses/>.
17
18For support and more information about Cafu, visit us at <http://www.cafu.de>.
19=================================================================================
20*/
21
22#include <iomanip>
23#include <iostream>
24#include <fstream>
25#include <string>
26#include <limits>
27
28#include <ft2build.h>
29#include FT_FREETYPE_H
30
31#include "Bitmap/Bitmap.hpp"
32#include "ConsoleCommands/Console.hpp"
33#include "ConsoleCommands/ConsoleStdout.hpp"
34#include "FileSys/FileManImpl.hpp"
35#include "Templates/Array.hpp"
36
37#if defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER<1300)
38    #define vsnprintf _vsnprintf
39#endif
40
41
42static cf::ConsoleStdoutT ConsoleStdout;
43cf::ConsoleI* Console=&ConsoleStdout;
44
45static cf::FileSys::FileManImplT FileManImpl;
46cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl;
47
48
49bool DebugPNGs=false;
50
51
52struct GlyphInfoT
53{
54    unsigned long GlyphIndex;
55
56    float BearingX;     ///< The horizontal offset of the bitmap relative to the cursor position.
57    float BearingY;     ///< The vertical   offset of the bitmap relative to the cursor position (y-axis points up!).
58    float AdvanceX;     ///< How much the cursor position should be advanced horizontally for rendering the next character.
59
60    int   Width;        ///< The width  in pixels of the bitmap of this glyph.
61    int   Height;       ///< The height in pixels of the bitmap of this glyph.
62
63    int   BitmapNr;     ///< The index of the larger bitmap this glyphs bitmap is embedded in.
64    float s1;           ///< The s1 tex-coord into the larger bitmap.
65    float t1;           ///< The t1 tex-coord into the larger bitmap.
66    float s2;           ///< The s2 tex-coord into the larger bitmap.
67    float t2;           ///< The t2 tex-coord into the larger bitmap.
68};
69
70
71std::string va(const char* FormatString, ...)
72{
73    va_list ArgList;
74    char    Buffer[1024];
75
76    if (!FormatString) return "";
77
78    va_start(ArgList, FormatString);
79    vsnprintf(Buffer, 1024-1, FormatString, ArgList);
80    Buffer[1024-1]=0;
81    va_end(ArgList);
82
83    return Buffer;
84}
85
86
87/// This class "allocates" rectangular areas in larger bitmaps.
88class RectBitmapAllocatorT
89{
90    public:
91
92    RectBitmapAllocatorT(uint32_t BitmapInitColor, unsigned long BitmapSizeS=256, unsigned long BitmapSizeT=256)
93        : BITMAP_SIZE_S(BitmapSizeS),
94          BITMAP_SIZE_T(BitmapSizeT),
95          BITMAP_INIT_COLOR(BitmapInitColor)
96    {
97        for (unsigned long s=0; s<BITMAP_SIZE_S; s++)
98            BitmapAllocated.PushBack(BITMAP_SIZE_T);
99    }
100
101    ~RectBitmapAllocatorT()
102    {
103        for (unsigned long BitmapNr=0; BitmapNr<Bitmaps.Size(); BitmapNr++)
104            delete Bitmaps[BitmapNr];
105    }
106
107    bool Allocate(unsigned long SizeS, unsigned long SizeT, unsigned long& BitmapNr, unsigned long& PosS, unsigned long& PosT)
108    {
109        if (SizeS>BITMAP_SIZE_S) return false;
110        if (SizeT>BITMAP_SIZE_T) return false;
111
112        BitmapNr=Bitmaps.Size()-1;
113        if (AllocateHelper(SizeS, SizeT, PosS, PosT)) return true;
114
115        BitmapT* Bitmap=new BitmapT(BITMAP_SIZE_S, BITMAP_SIZE_T);
116        for (unsigned long i=0; i<Bitmap->Data.Size(); i++)
117            Bitmap->Data[i]=BITMAP_INIT_COLOR;
118        Bitmaps.PushBack(Bitmap);
119
120        for (unsigned long s=0; s<BITMAP_SIZE_S; s++)
121            BitmapAllocated[s]=0;
122
123        BitmapNr=Bitmaps.Size()-1;
124        return AllocateHelper(SizeS, SizeT, PosS, PosT);
125    }
126
127
128    const unsigned long BITMAP_SIZE_S;
129    const unsigned long BITMAP_SIZE_T;
130    const uint32_t      BITMAP_INIT_COLOR;
131    ArrayT<BitmapT*>    Bitmaps;
132
133
134    private:
135
136    RectBitmapAllocatorT(const RectBitmapAllocatorT&);      ///< Use of the Copy Constructor    is not allowed.
137    void operator = (const RectBitmapAllocatorT&);          ///< Use of the Assignment Operator is not allowed.
138
139    bool AllocateHelper(unsigned long SizeS, unsigned long SizeT, unsigned long& PosS, unsigned long& PosT)
140    {
141        unsigned long Best=BITMAP_SIZE_T;
142
143        for (unsigned long s=0; s<=BITMAP_SIZE_S-SizeS; s++)
144        {
145            unsigned long Best2=0;
146            unsigned long s2;
147
148            for (s2=0; s2<SizeS; s2++)
149            {
150                if (BitmapAllocated[s+s2]>=Best) break;
151                if (BitmapAllocated[s+s2]>Best2) Best2=BitmapAllocated[s+s2];
152            }
153
154            if (s2==SizeS)
155            {
156                // Gültige Position gefunden
157                PosS=s;
158                PosT=Best=Best2;
159            }
160        }
161
162        if (Best+SizeT>BITMAP_SIZE_T) return false;
163
164        for (unsigned long s=0; s<SizeS; s++) BitmapAllocated[PosS+s]=Best+SizeT;
165        return true;
166    }
167
168    ArrayT<unsigned long> BitmapAllocated;
169};
170
171
172void ProcessNewGlyph(FT_Face& Face, GlyphInfoT& Glyph, RectBitmapAllocatorT& RBA)
173{
174    unsigned long PosS=0;
175    unsigned long PosT=0;
176
177    // IMPORTANT:
178    // Our bitmaps are rendered by the Cafu engine using bilinear filtering.
179    // Therefore, we add a safety margin (a frame) of 1 pixel thickness around each glyph bitmap,
180    // i.e. we increase its width and height by two pixels.
181    // This safety margin is transparently added here in this function -- no user code is supposed to ever become aware of it!
182    unsigned long BitmapNr;
183    if (!RBA.Allocate(Face->glyph->bitmap.width+2, Face->glyph->bitmap.rows+2, BitmapNr, PosS, PosT))
184    {
185        std::cout << "WARNING: Unable to process glyph!\n";
186
187        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).
188        BitmapNr=0;
189        PosS=0;
190        PosT=0;
191    }
192
193    BitmapT* Bitmap=RBA.Bitmaps[BitmapNr];
194
195    // Copy the glyphs bitmap into the larger bitmap.
196    for (int y=0; y<Face->glyph->bitmap.rows; y++)
197        for (int x=0; x<Face->glyph->bitmap.width; x++)
198        {
199            const unsigned char GrayValue=Face->glyph->bitmap.buffer[x+y*Face->glyph->bitmap.pitch];
200
201            if (DebugPNGs) Bitmap->SetPixel(PosS+x+1, PosT+y+1, GrayValue, GrayValue, GrayValue, 255);
202                      else Bitmap->SetPixel(PosS+x+1, PosT+y+1, 255, 255, 255, GrayValue);
203        }
204
205
206    // Fill out the Glyph structure.
207    Glyph.BearingX=float(Face->glyph->bitmap_left-1);       // The -1 is required in order to compensate for our "safety frame".
208    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.)
209    Glyph.AdvanceX=float(Face->glyph->advance.x)/64.0f;
210 // Glyph.AdvanceX=float(Face->glyph->linearHoriAdvance)/65536.0f;
211
212    Glyph.Width =Face->glyph->bitmap.width+2;
213    Glyph.Height=Face->glyph->bitmap.rows +2;
214
215    Glyph.BitmapNr=BitmapNr;
216    Glyph.s1=float(PosS)/float(RBA.BITMAP_SIZE_S);
217    Glyph.t1=float(PosT)/float(RBA.BITMAP_SIZE_T);
218    Glyph.s2=float(PosS+Face->glyph->bitmap.width+2)/float(RBA.BITMAP_SIZE_S);
219    Glyph.t2=float(PosT+Face->glyph->bitmap.rows +2)/float(RBA.BITMAP_SIZE_T);
220}
221
222
223int Usage()
224{
225    std::cout << "\nUSAGE: MakeFont FontFileName [OPTIONS]\n";
226    std::cout << "\n";
227    std::cout << "OPTIONS:\n";
228    std::cout << "-m=MaterialBaseName\n";
229    std::cout << "   If this is given, a Font.cmat material definition file will be created\n";
230    std::cout << "   automatically, using MaterialBaseName as a basis for creating the material\n";
231    std::cout << "   names.\n";
232    std::cout << "\n";
233    std::cout << "-debug\n";
234    std::cout << "   Creates the FontImage_X_Y.png files with false colors.\n";
235    std::cout << "\n";
236    std::cout << "Example:\n";
237    std::cout << "   MakeFont c:\\WINNT\\Fonts\\arial.ttf -m=Arial\n";
238    return 1;
239}
240
241
242int main(int ArgC, const char* ArgV[])
243{
244    // Output header.
245    std::cout << "The Cafu Font Maker, version " << __DATE__ << ".\n\n";
246    std::cout << "Portions of this software are copyright (c) 2006 The FreeType Project\n(www.freetype.org). All rights reserved.\n\n";
247
248    // Initialize the FileMan by mounting the default file system.
249    // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of
250    // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application.
251    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", "");
252    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE");
253    cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE");
254
255    // Parse the command-line arguments.
256    std::string FontFileName="";
257    std::string MaterialBaseName="";
258
259    for (int ArgNr=1; ArgNr<ArgC; ArgNr++)
260    {
261             if (strncmp(ArgV[ArgNr], "-m=" , 3)==0) MaterialBaseName=ArgV[ArgNr]+3;
262        else if (strcmp (ArgV[ArgNr], "-debug" )==0) DebugPNGs=true;
263        else if (ArgV[ArgNr][0]=='-') return Usage();
264        else if (FontFileName=="") FontFileName=ArgV[ArgNr];
265        else return Usage();
266    }
267
268    if (FontFileName=="") return Usage();
269
270
271    // Init the FreeType library.
272    FT_Library ftLib;
273    if (FT_Init_FreeType(&ftLib)!=0)
274    {
275        std::cout << "Error: Could not init the FreeType library.\n";
276        return 1;
277    }
278
279
280    // Load the font face.
281    FT_Face ftFontFace;
282    if (FT_New_Face(ftLib, FontFileName.c_str(), 0, &ftFontFace)!=0)
283    {
284        std::cout << "Error: Could not load the font face from \"" << FontFileName << "\".\n";
285        return 1;
286    }
287
288
289    const int PointSizes[]= { 12, 24, 48 };     // The sizes in points (1/72th of an inch).
290
291    for (unsigned long SizeNr=0; SizeNr<3; SizeNr++)
292    {
293        const int SizeInPoints=PointSizes[SizeNr];
294
295        // Set the character size to SizeInPoints points at 72 DPI, which implies that the size in pixels is the same as SizeInPoints.
296        if (FT_Set_Char_Size(ftFontFace, SizeInPoints*64, 0, 72, 72)!=0)
297        {
298            std::cout << "Error: Could not set the character size to " << SizeInPoints << "pt.\n";
299            continue;
300        }
301
302        RectBitmapAllocatorT  RBA(DebugPNGs ? 0xFFFF00FF : 0x00FFFFFF);     // The default color is "invisible white", that is, RGB=1 and A=0.
303        ArrayT<unsigned long> CharToGlyphInfoNr;
304        ArrayT<GlyphInfoT>    GlyphInfos;
305
306        for (unsigned long CharNr=0; CharNr<256; CharNr++)
307        {
308         // const unsigned long GlyphIndex=(CharNr<128) ? FT_Get_Char_Index(ftFontFace, CharNr) : 0;
309            const unsigned long GlyphIndex=FT_Get_Char_Index(ftFontFace, CharNr);
310
311            // Determine if we already have a GlyphInfoT object with that GlyphIndex, that is, determine whether GlyphIndex has occurred before.
312            unsigned long GlyphInfoNr;
313
314            for (GlyphInfoNr=0; GlyphInfoNr<GlyphInfos.Size(); GlyphInfoNr++)
315                if (GlyphInfos[GlyphInfoNr].GlyphIndex==GlyphIndex)
316                    break;
317
318            CharToGlyphInfoNr.PushBack(GlyphInfoNr);
319
320            // If we already have such a GlyphInfoT, we're done with this character.
321            if (GlyphInfoNr<GlyphInfos.Size()) continue;
322
323            // Load the glyph at GlyphIndex into the glyph slot of ftFontFace (i.e. ftFontFace->glyph).
324            if (FT_Load_Glyph(ftFontFace, GlyphIndex, FT_LOAD_RENDER)!=0)
325            {
326                std::cout << "Error: Could not obtain the glyph at index " << GlyphIndex << ".\n";
327                continue;   // TODO: Is this proper error handling???
328            }
329
330
331            // Insert and store the new glyph info.
332            GlyphInfos.PushBackEmpty();
333            GlyphInfos[GlyphInfos.Size()-1].GlyphIndex=GlyphIndex;
334
335            ProcessNewGlyph(ftFontFace, GlyphInfos[GlyphInfos.Size()-1], RBA);
336        }
337
338
339        // Save all resulting texture maps to disk.
340        for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++)
341            RBA.Bitmaps[TexMapNr]->SaveToDisk(va("FontImage_%i_%lu.png", SizeInPoints, TexMapNr).c_str());
342
343
344        // Save the resulting font description to disk.
345        std::ofstream FontFile(va("FontDescr_%i.cfont", SizeInPoints).c_str(), std::ios::out);
346        // if (FontFile.bad()) ...
347
348        const int sigdigits=std::numeric_limits<float>::digits10;
349        FontFile << std::setprecision(sigdigits);
350
351        FontFile << "// Values that are not per-glyph, but global to the font face at the current scale (" << SizeInPoints << " pixels).\n";
352        FontFile << "// The values are: ascender, descender and height.\n";
353        FontFile << "global " << float(ftFontFace->size->metrics.ascender )/64.0f << " "
354                              << float(ftFontFace->size->metrics.descender)/64.0f << " "
355                              << float(ftFontFace->size->metrics.height   )/64.0f << "\n";
356        FontFile << "\n";
357
358        FontFile << "// For each of the " << CharToGlyphInfoNr.Size() << " characters, record the index into the glyph infos below.\n";
359        for (unsigned long CharNr=0; CharNr<CharToGlyphInfoNr.Size(); CharNr++)
360        {
361            if (CharNr>0)
362            {
363                if ((CharNr % 16)==0) FontFile << "\n"; else FontFile << " ";
364            }
365
366            FontFile << CharToGlyphInfoNr[CharNr];
367        }
368        FontFile << "\n\n";
369
370        FontFile << "// The glyphs below refer to larger bitmaps, represented by the following materials.\n";
371        for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++)
372            FontFile << "matname Fonts/" << MaterialBaseName << "_" << SizeInPoints << "_" << TexMapNr << "\n";
373        FontFile << "\n";
374
375        FontFile << "// The glyphs (num, bearing x, bearing y, advance x, width, height, bitmap num, x1, y1, x2, y2).\n";
376        for (unsigned long GINr=0; GINr<GlyphInfos.Size(); GINr++)
377        {
378            const GlyphInfoT& Glyph=GlyphInfos[GINr];
379
380            FontFile << "glyph " << GINr
381                << " " << Glyph.BearingX
382                << " " << Glyph.BearingY
383                << " " << Glyph.AdvanceX
384                << " " << Glyph.Width
385                << " " << Glyph.Height
386                << " " << Glyph.BitmapNr
387                << " " << Glyph.s1
388                << " " << Glyph.t1
389                << " " << Glyph.s2
390                << " " << Glyph.t2
391                << "\n";
392        }
393        FontFile << "\n";
394
395        FontFile << "// The kerning table.\n";
396        for (unsigned long GI1Nr=0; GI1Nr<GlyphInfos.Size(); GI1Nr++)
397            for (unsigned long GI2Nr=0; GI2Nr<GlyphInfos.Size(); GI2Nr++)
398            {
399                const GlyphInfoT& gi1=GlyphInfos[GI1Nr];
400                const GlyphInfoT& gi2=GlyphInfos[GI2Nr];
401
402                FT_Vector KerningVec;
403
404                if (FT_Get_Kerning(ftFontFace, gi1.GlyphIndex, gi2.GlyphIndex, FT_KERNING_DEFAULT /*FT_KERNING_UNFITTED*/, &KerningVec)!=0) continue;
405                if (KerningVec.x==0) continue;
406
407                const float HorKerning=float(KerningVec.x)/64.0f;
408
409                FontFile << "k " << GI1Nr << " " << GI2Nr << " " << HorKerning << "\n";
410            }
411
412
413        // Auto-create a materials definition file if MaterialBaseName was given.
414        if (MaterialBaseName!="")
415        {
416            std::ofstream CMatFile("Font.cmat", SizeNr==0 ? std::ios::out : std::ios::out | std::ios::app);
417
418            for (unsigned long TexMapNr=0; TexMapNr<RBA.Bitmaps.Size(); TexMapNr++)
419            {
420                CMatFile << "Fonts/" << MaterialBaseName << "_" << SizeInPoints << "_" << TexMapNr << "\n";     // Fonts/Arial_24_0
421                CMatFile << "{\n";
422                CMatFile << "    diffusemap " << "FontImage" /*MaterialBaseName*/ << "_" << SizeInPoints << "_" << TexMapNr << ".png, minFilter linear, noScaleDown\n";
423                CMatFile << "\n";
424                CMatFile << "    noDynLight    // Should be replaced e.g. by \"LightShader none\"...\n";
425                CMatFile << "\n";
426                CMatFile << "    blendFunc src_alpha one_minus_src_alpha\n";
427                CMatFile << "    red   ambientLightRed       // Hmmm. Maybe we should rather use fParam0...fParam2 here.\n";
428                CMatFile << "    green ambientLightGreen\n";
429                CMatFile << "    blue  ambientLightBlue\n";
430                CMatFile << "    ambientMask d               // Don't write into the depth buffer!\n";
431                CMatFile << "}\n";
432                CMatFile << "\n";
433            }
434        }
435    }
436
437
438    // Clean up and quit.
439    FT_Done_Face(ftFontFace);
440    FT_Done_FreeType(ftLib);
441    return 0;
442}
Note: See TracBrowser for help on using the browser.