1 #include "Font.h"
2 
3 #include "../core/LogManager.h"
4 using namespace jvgs::core;
5 
6 #include "../sketch/Group.h"
7 #include "../sketch/Path.h"
8 #include "../sketch/PathComponent.h"
9 using namespace jvgs::sketch;
10 
11 #include "../math/CubicCurve.h"
12 #include "../math/LineSegment.h"
13 #include "../math/QuadraticCurve.h"
14 using namespace jvgs::math;
15 
16 #include <vector>
17 using namespace std;
18 
19 #include "../video/VideoManager.h"
20 #include "../video/SketchyRenderer.h"
21 using namespace jvgs::video;
22 
23 namespace jvgs
24 {
25     namespace font
26     {
27         const int Font::NUMBER_OF_CHARACTERS = 256;
28 
Font(const string & fileName,float size)29         Font::Font(const string &fileName, float size)
30         {
31             /* Some references. */
32             ListManager *listManager = ListManager::getInstance();
33             VideoManager *videoManager = VideoManager::getInstance();
34             LogManager *logManager = LogManager::getInstance();
35 
36             advances = new float[NUMBER_OF_CHARACTERS];
37 
38             this->size = size;
39 
40             /* Matrix to convert points. */
41             matrix = AffineTransformationMatrix();
42             matrix.scale(Vector2D(1.0f / 64.0f, 1.0f / 64.0f));
43             matrix.scale(Vector2D(1.0f, -1.0f));
44 
45             /* Generate lists. */
46             base = listManager->createLists(NUMBER_OF_CHARACTERS);
47 
48             /* Create the main library and init it. */
49             FT_Library library;
50             if(FT_Init_FreeType(&library))
51                 logManager->error("Could not create main FreeType library.");
52 
53             /* Load the actual font. */
54             FT_Face face;
55             if(FT_New_Face(library, fileName.c_str(), 0, &face))
56                 logManager->error("Could not open '%s'.", fileName.c_str());
57 
58             /* Set the pixel size. */
59             FT_Set_Pixel_Sizes(face, 0, (FT_UInt) size);
60 
61             /* Load characters. */
62             for(int i = 0; i < NUMBER_OF_CHARACTERS; i++) {
63                 /* Get the glyph. */
64                 FT_UInt index = FT_Get_Char_Index(face, i);
65                 if(!FT_Load_Glyph(face, index, FT_LOAD_DEFAULT)) {
66                     FT_GlyphSlot glyph = face->glyph;
67 
68                     /* Create the outline. */
69                     FT_Outline outline = glyph->outline;
70                     Group *group = createSketchGroup(&outline);
71 
72                     /* Start rendering. */
73                     listManager->beginList(base + i);
74 
75                     Renderer *renderer = new SketchyRenderer();
76                     group->render(renderer);
77                     delete renderer;
78                     delete group;
79 
80                     /* Advance for next character. */
81                     Vector2D advance = toVector(glyph->advance);
82                     videoManager->translate(advance);
83                     advances[i] = advance.getX();
84 
85                     /* End render. */
86                     listManager->endList();
87                 /* Loading the glyph failed, but we create a dummy list anyway
88                  * so we don't get errors when rendering. */
89                 } else {
90                     listManager->beginList(base + i);
91                     listManager->endList();
92                 }
93             }
94 
95             /* Clean up. */
96             FT_Done_Face(face);
97             FT_Done_FreeType(library);
98         }
99 
~Font()100         Font::~Font()
101         {
102             ListManager::getInstance()->deleteLists(base, NUMBER_OF_CHARACTERS);
103             delete[] advances;
104         }
105 
drawString(const string & string) const106         void Font::drawString(const string &string) const
107         {
108             VideoManager::getInstance()->push();
109             ListManager *listManager = ListManager::getInstance();
110             listManager->callLists(base, (GLubyte*) string.c_str(),
111                     (int) string.size());
112             VideoManager::getInstance()->pop();
113         }
114 
getStringWidth(const string & string) const115         float Font::getStringWidth(const string &string) const
116         {
117             float width = 0;
118 
119             for(string::size_type i = 0; i < string.size(); i++)
120                 width += advances[string[i]];
121 
122             return width;
123         }
124 
createSketchGroup(FT_Outline * outline) const125         Group *Font::createSketchGroup(FT_Outline *outline) const
126         {
127             Group *root = new Group(0);
128             Path *path = new Path(root);
129             PathComponent *component = new PathComponent(path);
130 
131             vector<Vector2D> onPoints;
132             vector<Vector2D> quadraticPoints;
133             vector<Vector2D> cubicPoints;
134 
135             /* Countour and point index. */
136             int c = 0, p = 0;
137             while(c < outline->n_contours && p < outline->n_points) {
138 
139                 /* Add point. */
140                 Vector2D point = toVector(outline->points[p]);
141                 if(FT_CURVE_TAG(outline->tags[p]) == FT_CURVE_TAG_ON)
142                     onPoints.push_back(point);
143                 else if(FT_CURVE_TAG(outline->tags[p]) == FT_CURVE_TAG_CONIC)
144                     quadraticPoints.push_back(point);
145                 else if(FT_CURVE_TAG(outline->tags[p]) == FT_CURVE_TAG_CUBIC)
146                     cubicPoints.push_back(point);
147 
148                 /* A cubic curve matches. */
149                 if(cubicPoints.size() == 2 && onPoints.size() == 2) {
150                     CubicCurve *curve = new CubicCurve(onPoints[0],
151                             cubicPoints[0], cubicPoints[1], onPoints[1]);
152                     component->addSegment(curve);
153                     cubicPoints.clear();
154                     onPoints.erase(onPoints.begin());
155                 }
156 
157                 /* Insert "off" points. See freetype documentation. */
158                 if(quadraticPoints.size() == 2 && onPoints.size() < 2) {
159                     Vector2D middle = quadraticPoints[0] + quadraticPoints[1];
160                     middle *= 0.5f;
161                     onPoints.push_back(middle);
162                 }
163 
164                 /* Quadratic curve matches. Could be more than one. */
165                 while(quadraticPoints.size() >= 1 && onPoints.size() >= 2) {
166                     QuadraticCurve *curve = new QuadraticCurve(onPoints[0],
167                             quadraticPoints[0], onPoints[1]);
168                     component->addSegment(curve);
169                     quadraticPoints.erase(quadraticPoints.begin());
170                     onPoints.erase(onPoints.begin());
171                 }
172 
173                 /* Straight line segment matches. */
174                 if(onPoints.size() == 2 && quadraticPoints.size() <= 0 &&
175                         cubicPoints.size() <= 0) {
176                     LineSegment *segment = new LineSegment(onPoints[0],
177                             onPoints[1]);
178                     component->addSegment(segment);
179                     onPoints.erase(onPoints.begin());
180                 }
181 
182                 /* End of a contour. */
183                 if(p == outline->contours[c]) {
184                     onPoints.clear();
185                     quadraticPoints.clear();
186                     cubicPoints.clear();
187                     component->close();
188                     path->addComponent(component);
189                     component = new PathComponent(path);
190                     c++;
191                 }
192 
193                 p++;
194             }
195 
196             component->close();
197             path->addComponent(component);
198             root->addSketchElement(path);
199 
200             return root;
201         }
202 
toVector(const FT_Vector & point) const203         Vector2D Font::toVector(const FT_Vector &point) const
204         {
205             return matrix * Vector2D((float) point.x, (float) point.y);
206         }
207     }
208 }
209