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@hocevar.net>
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     (void)vertex_data; (void)weight;
62 
63     const FTGL_DOUBLE* vertex = static_cast<const FTGL_DOUBLE*>(coords);
64     *outData = const_cast<FTGL_DOUBLE*>(mesh->Combine(vertex[0], vertex[1], vertex[2]));
65 }
66 
ftglBegin(GLenum type,FTMesh * mesh)67 void CALLBACK ftglBegin(GLenum type, FTMesh* mesh)
68 {
69     mesh->Begin(type);
70 }
71 
72 
ftglEnd(FTMesh * mesh)73 void CALLBACK ftglEnd(FTMesh* mesh)
74 {
75     mesh->End();
76 }
77 
78 
FTMesh()79 FTMesh::FTMesh()
80 : currentTesselation(0),
81     err(0)
82 {
83     tesselationList.reserve(16);
84 }
85 
86 
~FTMesh()87 FTMesh::~FTMesh()
88 {
89     for(size_t t = 0; t < tesselationList.size(); ++t)
90     {
91         delete tesselationList[t];
92     }
93 
94     tesselationList.clear();
95 }
96 
97 
AddPoint(const FTGL_DOUBLE x,const FTGL_DOUBLE y,const FTGL_DOUBLE z)98 void FTMesh::AddPoint(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z)
99 {
100     currentTesselation->AddPoint(x, y, z);
101 }
102 
103 
Combine(const FTGL_DOUBLE x,const FTGL_DOUBLE y,const FTGL_DOUBLE z)104 const FTGL_DOUBLE* FTMesh::Combine(const FTGL_DOUBLE x, const FTGL_DOUBLE y, const FTGL_DOUBLE z)
105 {
106     tempPointList.push_back(FTPoint(x, y,z));
107     return static_cast<const FTGL_DOUBLE*>(tempPointList.back());
108 }
109 
110 
Begin(GLenum meshType)111 void FTMesh::Begin(GLenum meshType)
112 {
113     currentTesselation = new FTTesselation(meshType);
114 }
115 
116 
End()117 void FTMesh::End()
118 {
119     tesselationList.push_back(currentTesselation);
120 }
121 
122 
Tesselation(size_t index) const123 FTTesselation const * FTMesh::Tesselation(size_t index) const
124 {
125     return (index < tesselationList.size()) ? tesselationList[index] : NULL;
126 }
127 
128 
FTVectoriser(const FT_GlyphSlot glyph)129 FTVectoriser::FTVectoriser(const FT_GlyphSlot glyph)
130 :   contourList(0),
131     mesh(0),
132     ftContourCount(0),
133     contourFlag(0)
134 {
135     if(glyph)
136     {
137         outline = glyph->outline;
138 
139         ftContourCount = outline.n_contours;
140         contourList = 0;
141         contourFlag = outline.flags;
142 
143         ProcessContours();
144     }
145 }
146 
147 
~FTVectoriser()148 FTVectoriser::~FTVectoriser()
149 {
150     for(size_t c = 0; c < ContourCount(); ++c)
151     {
152         delete contourList[c];
153     }
154 
155     delete [] contourList;
156     delete mesh;
157 }
158 
159 
ProcessContours()160 void FTVectoriser::ProcessContours()
161 {
162     short contourLength = 0;
163     short startIndex = 0;
164     short endIndex = 0;
165 
166     contourList = new FTContour*[ftContourCount];
167 
168     for(int i = 0; i < ftContourCount; ++i)
169     {
170         FT_Vector* pointList = &outline.points[startIndex];
171         char* tagList = &outline.tags[startIndex];
172 
173         endIndex = outline.contours[i];
174         contourLength =  (endIndex - startIndex) + 1;
175 
176         FTContour* contour = new FTContour(pointList, tagList, contourLength);
177 
178         contourList[i] = contour;
179 
180         startIndex = endIndex + 1;
181     }
182 
183     // Compute each contour's parity. FIXME: see if FT_Outline_Get_Orientation
184     // can do it for us.
185     for(int i = 0; i < ftContourCount; i++)
186     {
187         FTContour *c1 = contourList[i];
188 
189         // 1. Find the leftmost point.
190         FTPoint leftmost(65536.0, 0.0);
191 
192         for(size_t n = 0; n < c1->PointCount(); n++)
193         {
194             FTPoint p = c1->Point(n);
195             if(p.X() < leftmost.X())
196             {
197                 leftmost = p;
198             }
199         }
200 
201         // 2. Count how many other contours we cross when going further to
202         // the left.
203         int parity = 0;
204 
205         for(int j = 0; j < ftContourCount; j++)
206         {
207             if(j == i)
208             {
209                 continue;
210             }
211 
212             FTContour *c2 = contourList[j];
213 
214             for(size_t n = 0; n < c2->PointCount(); n++)
215             {
216                 FTPoint p1 = c2->Point(n);
217                 FTPoint p2 = c2->Point((n + 1) % c2->PointCount());
218 
219                 /* FIXME: combinations of >= > <= and < do not seem stable */
220                 if((p1.Y() < leftmost.Y() && p2.Y() < leftmost.Y())
221                     || (p1.Y() >= leftmost.Y() && p2.Y() >= leftmost.Y())
222                     || (p1.X() > leftmost.X() && p2.X() > leftmost.X()))
223                 {
224                     continue;
225                 }
226                 else if(p1.X() < leftmost.X() && p2.X() < leftmost.X())
227                 {
228                     parity++;
229                 }
230                 else
231                 {
232                     FTPoint a = p1 - leftmost;
233                     FTPoint b = p2 - leftmost;
234                     if(b.X() * a.Y() > b.Y() * a.X())
235                     {
236                         parity++;
237                     }
238                 }
239             }
240         }
241 
242         // 3. Make sure the glyph has the proper parity.
243         c1->SetParity(parity);
244     }
245 }
246 
247 
PointCount()248 size_t FTVectoriser::PointCount()
249 {
250     size_t s = 0;
251     for(size_t c = 0; c < ContourCount(); ++c)
252     {
253         s += contourList[c]->PointCount();
254     }
255 
256     return s;
257 }
258 
259 
Contour(size_t index) const260 FTContour const * FTVectoriser::Contour(size_t index) const
261 {
262     return (index < ContourCount()) ? contourList[index] : NULL;
263 }
264 
265 
MakeMesh(FTGL_DOUBLE zNormal,int outsetType,float outsetSize)266 void FTVectoriser::MakeMesh(FTGL_DOUBLE zNormal, int outsetType, float outsetSize)
267 {
268     if(mesh)
269     {
270         delete mesh;
271     }
272 
273     mesh = new FTMesh;
274 
275     GLUtesselator* tobj = gluNewTess();
276 
277     gluTessCallback(tobj, GLU_TESS_BEGIN_DATA,     (GLUTesselatorFunction)ftglBegin);
278     gluTessCallback(tobj, GLU_TESS_VERTEX_DATA,    (GLUTesselatorFunction)ftglVertex);
279     gluTessCallback(tobj, GLU_TESS_COMBINE_DATA,   (GLUTesselatorFunction)ftglCombine);
280     gluTessCallback(tobj, GLU_TESS_END_DATA,       (GLUTesselatorFunction)ftglEnd);
281     gluTessCallback(tobj, GLU_TESS_ERROR_DATA,     (GLUTesselatorFunction)ftglError);
282 
283     if(contourFlag & ft_outline_even_odd_fill) // ft_outline_reverse_fill
284     {
285         gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
286     }
287     else
288     {
289         gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
290     }
291 
292 
293     gluTessProperty(tobj, GLU_TESS_TOLERANCE, 0);
294     gluTessNormal(tobj, 0.0f, 0.0f, zNormal);
295     gluTessBeginPolygon(tobj, mesh);
296 
297         for(size_t c = 0; c < ContourCount(); ++c)
298         {
299             /* Build the */
300             switch(outsetType)
301             {
302                 case 1 : contourList[c]->buildFrontOutset(outsetSize); break;
303                 case 2 : contourList[c]->buildBackOutset(outsetSize); break;
304             }
305             const FTContour* contour = contourList[c];
306 
307 
308             gluTessBeginContour(tobj);
309                 for(size_t p = 0; p < contour->PointCount(); ++p)
310                 {
311                     const FTGL_DOUBLE* d;
312                     switch(outsetType)
313                     {
314                         case 1: d = contour->FrontPoint(p); break;
315                         case 2: d = contour->BackPoint(p); break;
316                         case 0: default: d = contour->Point(p); break;
317                     }
318                     // XXX: gluTessVertex doesn't modify the data but does not
319                     // specify "const" in its prototype, so we cannot cast to
320                     // a const type.
321                     GLdouble* dd = const_cast<GLdouble*>(d);
322                     gluTessVertex(tobj, dd, static_cast<GLvoid*>(dd));
323                 }
324 
325             gluTessEndContour(tobj);
326         }
327     gluTessEndPolygon(tobj);
328 
329     gluDeleteTess(tobj);
330 }
331 
332