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