1 /*
2  * FTGL - OpenGL font library
3  *
4  * Copyright (c) 2001-2004 Henry Maddocks <ftgl@opengl.geek.nz>
5  * Copyright (c) 2008 Éric Beets <ericbeets@free.fr>
6  * Copyright (c) 2008 Sam Hocevar <sam@zoy.org>
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining
9  * a copy of this software and associated documentation files (the
10  * "Software"), to deal in the Software without restriction, including
11  * without limitation the rights to use, copy, modify, merge, publish,
12  * distribute, sublicense, and/or sell copies of the Software, and to
13  * permit persons to whom the Software is furnished to do so, subject to
14  * the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include "config.h"
29 
30 #include "FTInternals.h"
31 #include "FTVectoriser.h"
32 
33 #ifndef CALLBACK
34 #define CALLBACK
35 #endif
36 
37 #if defined __APPLE_CC__ && __APPLE_CC__ < 5465
38     typedef GLvoid (*GLUTesselatorFunction) (...);
39 #elif defined WIN32 && !defined __CYGWIN__
40     typedef GLvoid (CALLBACK *GLUTesselatorFunction) ();
41 #else
42     typedef GLvoid (*GLUTesselatorFunction) ();
43 #endif
44 
45 
ftglError(GLenum errCode,FTMesh * mesh)46 void CALLBACK ftglError(GLenum errCode, FTMesh* mesh)
47 {
48     mesh->Error(errCode);
49 }
50 
51 
ftglVertex(void * data,FTMesh * mesh)52 void CALLBACK ftglVertex(void* data, FTMesh* mesh)
53 {
54     FTGL_DOUBLE* vertex = static_cast<FTGL_DOUBLE*>(data);
55     mesh->AddPoint(vertex[0], vertex[1], vertex[2]);
56 }
57 
58 
ftglCombine(FTGL_DOUBLE coords[3],void * vertex_data[4],GLfloat weight[4],void ** outData,FTMesh * mesh)59 void CALLBACK ftglCombine(FTGL_DOUBLE coords[3], void* vertex_data[4], GLfloat weight[4], void** outData, FTMesh* mesh)
60 {
61     const FTGL_DOUBLE* vertex = static_cast<const FTGL_DOUBLE*>(coords);
62     *outData = const_cast<FTGL_DOUBLE*>(mesh->Combine(vertex[0], vertex[1], vertex[2]));
63 }
64 
ftglBegin(GLenum type,FTMesh * mesh)65 void CALLBACK ftglBegin(GLenum type, FTMesh* mesh)
66 {
67     mesh->Begin(type);
68 }
69 
70 
ftglEnd(FTMesh * mesh)71 void CALLBACK ftglEnd(FTMesh* mesh)
72 {
73     mesh->End();
74 }
75 
76 
FTMesh()77 FTMesh::FTMesh()
78 : currentTesselation(0),
79     err(0)
80 {
81     tesselationList.reserve(16);
82 }
83 
84 
~FTMesh()85 FTMesh::~FTMesh()
86 {
87     for(size_t t = 0; t < tesselationList.size(); ++t)
88     {
89         delete tesselationList[t];
90     }
91 
92     tesselationList.clear();
93 }
94 
95 
AddPoint(const FTGL_DOUBLE x,const FTGL_DOUBLE y,const FTGL_DOUBLE z)96 void FTMesh::AddPoint(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z)
97 {
98     currentTesselation->AddPoint(x, y, z);
99 }
100 
101 
Combine(const FTGL_DOUBLE x,const FTGL_DOUBLE y,const FTGL_DOUBLE z)102 const FTGL_DOUBLE* FTMesh::Combine(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z)
103 {
104     tempPointList.push_back(FTPoint(x, y,z));
105     return static_cast<const FTGL_DOUBLE*>(tempPointList.back());
106 }
107 
108 
Begin(GLenum meshType)109 void FTMesh::Begin(GLenum meshType)
110 {
111     currentTesselation = new FTTesselation(meshType);
112 }
113 
114 
End()115 void FTMesh::End()
116 {
117     tesselationList.push_back(currentTesselation);
118 }
119 
120 
Tesselation(size_t index) const121 const FTTesselation* const FTMesh::Tesselation(size_t index) const
122 {
123     return (index < tesselationList.size()) ? tesselationList[index] : NULL;
124 }
125 
126 
FTVectoriser(const FT_GlyphSlot glyph)127 FTVectoriser::FTVectoriser(const FT_GlyphSlot glyph)
128 :   contourList(0),
129     mesh(0),
130     ftContourCount(0),
131     contourFlag(0)
132 {
133     if(glyph)
134     {
135         outline = glyph->outline;
136 
137         ftContourCount = outline.n_contours;
138         contourList = 0;
139         contourFlag = outline.flags;
140 
141         ProcessContours();
142     }
143 }
144 
145 
~FTVectoriser()146 FTVectoriser::~FTVectoriser()
147 {
148     for(size_t c = 0; c < ContourCount(); ++c)
149     {
150         delete contourList[c];
151     }
152 
153     delete [] contourList;
154     delete mesh;
155 }
156 
157 
ProcessContours()158 void FTVectoriser::ProcessContours()
159 {
160     short contourLength = 0;
161     short startIndex = 0;
162     short endIndex = 0;
163 
164     contourList = new FTContour*[ftContourCount];
165 
166     for(int i = 0; i < ftContourCount; ++i)
167     {
168         FT_Vector* pointList = &outline.points[startIndex];
169         char* tagList = &outline.tags[startIndex];
170 
171         endIndex = outline.contours[i];
172         contourLength =  (endIndex - startIndex) + 1;
173 
174         FTContour* contour = new FTContour(pointList, tagList, contourLength);
175 
176         contourList[i] = contour;
177 
178         startIndex = endIndex + 1;
179     }
180 
181     // Compute each contour's parity. FIXME: see if FT_Outline_Get_Orientation
182     // can do it for us.
183     for(int i = 0; i < ftContourCount; i++)
184     {
185         FTContour *c1 = contourList[i];
186 
187         // 1. Find the leftmost point.
188         FTPoint leftmost(65536.0, 0.0);
189 
190         for(size_t n = 0; n < c1->PointCount(); n++)
191         {
192             FTPoint p = c1->Point(n);
193             if(p.X() < leftmost.X())
194             {
195                 leftmost = p;
196             }
197         }
198 
199         // 2. Count how many other contours we cross when going further to
200         // the left.
201         int parity = 0;
202 
203         for(int j = 0; j < ftContourCount; j++)
204         {
205             if(j == i)
206             {
207                 continue;
208             }
209 
210             FTContour *c2 = contourList[j];
211 
212             for(size_t n = 0; n < c2->PointCount(); n++)
213             {
214                 FTPoint p1 = c2->Point(n);
215                 FTPoint p2 = c2->Point((n + 1) % c2->PointCount());
216 
217                 /* FIXME: combinations of >= > <= and < do not seem stable */
218                 if((p1.Y() < leftmost.Y() && p2.Y() < leftmost.Y())
219                     || (p1.Y() >= leftmost.Y() && p2.Y() >= leftmost.Y())
220                     || (p1.X() > leftmost.X() && p2.X() > leftmost.X()))
221                 {
222                     continue;
223                 }
224                 else if(p1.X() < leftmost.X() && p2.X() < leftmost.X())
225                 {
226                     parity++;
227                 }
228                 else
229                 {
230                     FTPoint a = p1 - leftmost;
231                     FTPoint b = p2 - leftmost;
232                     if(b.X() * a.Y() > b.Y() * a.X())
233                     {
234                         parity++;
235                     }
236                 }
237             }
238         }
239 
240         // 3. Make sure the glyph has the proper parity.
241         c1->SetParity(parity);
242     }
243 }
244 
245 
PointCount()246 size_t FTVectoriser::PointCount()
247 {
248     size_t s = 0;
249     for(size_t c = 0; c < ContourCount(); ++c)
250     {
251         s += contourList[c]->PointCount();
252     }
253 
254     return s;
255 }
256 
257 
Contour(size_t index) const258 const FTContour* const FTVectoriser::Contour(size_t index) const
259 {
260     return (index < ContourCount()) ? contourList[index] : NULL;
261 }
262 
263 
MakeMesh(FTGL_DOUBLE zNormal,int outsetType,float outsetSize)264 void FTVectoriser::MakeMesh(FTGL_DOUBLE zNormal, int outsetType, float outsetSize)
265 {
266     if(mesh)
267     {
268         delete mesh;
269     }
270 
271     mesh = new FTMesh;
272 
273     GLUtesselator* tobj = gluNewTess();
274 
275     gluTessCallback(tobj, GLU_TESS_BEGIN_DATA,     (GLUTesselatorFunction)ftglBegin);
276     gluTessCallback(tobj, GLU_TESS_VERTEX_DATA,    (GLUTesselatorFunction)ftglVertex);
277     gluTessCallback(tobj, GLU_TESS_COMBINE_DATA,   (GLUTesselatorFunction)ftglCombine);
278     gluTessCallback(tobj, GLU_TESS_END_DATA,       (GLUTesselatorFunction)ftglEnd);
279     gluTessCallback(tobj, GLU_TESS_ERROR_DATA,     (GLUTesselatorFunction)ftglError);
280 
281     if(contourFlag & ft_outline_even_odd_fill) // ft_outline_reverse_fill
282     {
283         gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
284     }
285     else
286     {
287         gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
288     }
289 
290 
291     gluTessProperty(tobj, GLU_TESS_TOLERANCE, 0);
292     gluTessNormal(tobj, 0.0f, 0.0f, zNormal);
293     gluTessBeginPolygon(tobj, mesh);
294 
295         for(size_t c = 0; c < ContourCount(); ++c)
296         {
297             /* Build the */
298             switch(outsetType)
299             {
300                 case 1 : contourList[c]->buildFrontOutset(outsetSize); break;
301                 case 2 : contourList[c]->buildBackOutset(outsetSize); break;
302             }
303             const FTContour* contour = contourList[c];
304 
305 
306             gluTessBeginContour(tobj);
307                 for(size_t p = 0; p < contour->PointCount(); ++p)
308                 {
309                     const FTGL_DOUBLE* d;
310                     switch(outsetType)
311                     {
312                         case 1: d = contour->FrontPoint(p); break;
313                         case 2: d = contour->BackPoint(p); break;
314                         case 0: default: d = contour->Point(p); break;
315                     }
316                     // XXX: gluTessVertex doesn't modify the data but does not
317                     // specify "const" in its prototype, so we cannot cast to
318                     // a const type.
319                     gluTessVertex(tobj, (GLdouble *)d, (GLvoid *)d);
320                 }
321 
322             gluTessEndContour(tobj);
323         }
324     gluTessEndPolygon(tobj);
325 
326     gluDeleteTess(tobj);
327 }
328 
329