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