1 /* QuesoGLC
2 * A free implementation of the OpenGL Character Renderer (GLC)
3 * Copyright (c) 2002, 2004-2009, Bertrand Coconnier
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 /* $Id: misc.c 887 2009-03-24 01:03:11Z bcoconni $ */
20
21 /** \file
22 * defines miscellaneous utility routines used throughout QuesoGLC.
23 */
24
25 #include <math.h>
26
27 #include "internal.h"
28
29
30 #ifdef DEBUGMODE
31 GLCAPI GLuint __glcMemAllocCount = 0;
32 GLCAPI GLuint __glcMemAllocTrigger = 0;
33 GLCAPI GLboolean __glcMemAllocFailOnce = GL_TRUE;
34 #endif
35
36 /* QuesoGLC own allocation and memory management routines */
__glcMalloc(size_t size)37 void* __glcMalloc(size_t size)
38 {
39 #ifdef DEBUGMODE
40 __glcMemAllocCount++;
41
42 if (__glcMemAllocFailOnce) {
43 if (__glcMemAllocCount == __glcMemAllocTrigger)
44 return NULL;
45 }
46 else if (__glcMemAllocCount >= __glcMemAllocTrigger)
47 return NULL;
48 #endif
49 return malloc(size);
50 }
51
__glcFree(void * ptr)52 void __glcFree(void *ptr)
53 {
54 /* Not all implementations of free() accept NULL. Moreover this allows to
55 * detect useless calls.
56 */
57 assert(ptr);
58
59 free(ptr);
60 }
61
__glcRealloc(void * ptr,size_t size)62 void* __glcRealloc(void *ptr, size_t size)
63 {
64 #ifdef DEBUGMODE
65 __glcMemAllocCount++;
66
67 if (__glcMemAllocFailOnce) {
68 if (__glcMemAllocCount == __glcMemAllocTrigger)
69 return NULL;
70 }
71 else if (__glcMemAllocCount >= __glcMemAllocTrigger)
72 return NULL;
73 #endif
74 return realloc(ptr, size);
75 }
76
77
78
79 /* Find a token in a list of tokens separated by 'separator' */
__glcFindIndexList(GLCchar8 * inString,GLuint inIndex,const GLCchar8 * inSeparator)80 GLCchar8* __glcFindIndexList(GLCchar8* inString, GLuint inIndex,
81 const GLCchar8* inSeparator)
82 {
83 GLuint occurence = 0;
84 GLCchar8* s = inString;
85 const GLCchar8* sep = inSeparator;
86
87 if (!inIndex)
88 return s;
89
90 for (; *s != '\0'; s++) {
91 if (*s == *sep) {
92 occurence++;
93 if (occurence == inIndex)
94 break;
95 }
96 }
97
98 return (GLCchar8 *) s;
99 }
100
101
102
103 #ifndef HAVE_TLS
104 /* Each thread has to store specific informations so they can be retrieved
105 * later. __glcGetThreadArea() returns a struct which contains thread specific
106 * info for GLC.
107 * If the '__GLCthreadArea' of the current thread does not exist, it is created
108 * and initialized.
109 * IMPORTANT NOTE : __glcGetThreadArea() must never use __glcMalloc() and
110 * __glcFree() since those functions could use the exceptContextStack
111 * before it is initialized.
112 */
__glcGetThreadArea(void)113 __GLCthreadArea* __glcGetThreadArea(void)
114 {
115 __GLCthreadArea *area = NULL;
116
117 #ifdef __WIN32__
118 area = (__GLCthreadArea*)TlsGetValue(__glcCommonArea.threadKey);
119 #else
120 area = (__GLCthreadArea*)pthread_getspecific(__glcCommonArea.threadKey);
121 #endif
122 if (area)
123 return area;
124
125 area = (__GLCthreadArea*)malloc(sizeof(__GLCthreadArea));
126 if (!area)
127 return NULL;
128
129 area->currentContext = NULL;
130 area->errorState = GLC_NONE;
131 area->lockState = 0;
132 area->exceptionStack.head = NULL;
133 area->exceptionStack.tail = NULL;
134 area->failedTry = GLC_NO_EXC;
135 #ifdef __WIN32__
136 if (!TlsSetValue(__glcCommonArea.threadKey, (LPVOID)area)) {
137 free(area);
138 return NULL;
139 }
140 #else
141 pthread_setspecific(__glcCommonArea.threadKey, (void*)area);
142 #endif
143
144 #ifdef __WIN32__
145 if (__glcCommonArea.threadID == GetCurrentThreadId())
146 #else
147 if (pthread_equal(__glcCommonArea.threadID, pthread_self()))
148 #endif
149 __glcThreadArea = area;
150 return area;
151 }
152 #endif /* HAVE_TLS */
153
154
155
156 #ifndef HAVE_TLS
157 /* Raise an error. This function must be called each time the current error
158 * of the issuing thread must be set
159 */
__glcRaiseError(GLCenum inError)160 void __glcRaiseError(GLCenum inError)
161 {
162 GLCenum error = GLC_NONE;
163 __GLCthreadArea *area = NULL;
164
165 area = GLC_GET_THREAD_AREA();
166 assert(area);
167
168 /* An error can only be raised if the current error value is GLC_NONE.
169 * However, when inError == GLC_NONE then we must force the current error
170 * value to GLC_NONE whatever its previous value was.
171 */
172 error = area->errorState;
173 if (!error || !inError)
174 area->errorState = inError;
175 }
176 #endif /* HAVE_TLS */
177
178
179
180 #ifndef HAVE_TLS
181 /* Get the current context of the issuing thread */
__glcGetCurrent(void)182 __GLCcontext* __glcGetCurrent(void)
183 {
184 __GLCthreadArea *area = NULL;
185
186 area = __glcGetThreadArea(); /* Don't use GLC_GET_THREAD_AREA */
187 assert(area);
188
189 return area->currentContext;
190 }
191 #endif
192
193
194
195 /* Process the character in order to find a font that maps the code and to
196 * render the corresponding glyph. Replacement code and '<hexcode>' format
197 * are issued if necessary. The previous code is updated accordingly.
198 * 'inCode' must be given in UCS-4 format
199 */
__glcProcessChar(__GLCcontext * inContext,GLint inCode,__GLCcharacter * inPrevCode,GLboolean inIsRTL,__glcProcessCharFunc inProcessCharFunc,void * inProcessCharData)200 void* __glcProcessChar(__GLCcontext *inContext, GLint inCode,
201 __GLCcharacter* inPrevCode, GLboolean inIsRTL,
202 __glcProcessCharFunc inProcessCharFunc,
203 void* inProcessCharData)
204 {
205 GLint repCode = 0;
206 __GLCfont* font = NULL;
207 void* ret = NULL;
208
209 if (!inCode)
210 return NULL;
211
212 /* Get a font that maps inCode */
213 font = __glcContextGetFont(inContext, inCode);
214 if (font) {
215 /* A font has been found */
216 if (font != inPrevCode->font)
217 inPrevCode->code = 0; /* The font has changed, kerning must be disabled */
218 ret = inProcessCharFunc(inCode, inPrevCode->code, inIsRTL, font, inContext,
219 inProcessCharData, GL_FALSE);
220 inPrevCode->code = inCode;
221 inPrevCode->font = font;
222 return ret;
223 }
224
225 /* __glcCtxGetFont() can not find a font that maps inCode, we then attempt to
226 * produce an alternate rendering.
227 */
228
229 /* If the variable GLC_REPLACEMENT_CODE is nonzero, and __glcCtxGetFont()
230 * finds a font that maps the replacement code, we now render the character
231 * that the replacement code is mapped to
232 */
233 repCode = inContext->stringState.replacementCode;
234 font = __glcContextGetFont(inContext, repCode);
235 if (repCode && font) {
236 if (font != inPrevCode->font)
237 inPrevCode->code = 0; /* The font has changed, kerning must be disabled */
238 ret = inProcessCharFunc(repCode, inPrevCode->code, inIsRTL, font, inContext,
239 inProcessCharData, GL_FALSE);
240 inPrevCode->code = inCode;
241 inPrevCode->font = font;
242 return ret;
243 }
244 else {
245 /* If we get there, we failed to render both the character that inCode maps
246 * to and the replacement code. Now, we will try to render the character
247 * sequence "\<hexcode>", where '\' is the character REVERSE SOLIDUS
248 * (U+5C), '<' is the character LESS-THAN SIGN (U+3C), '>' is the character
249 * GREATER-THAN SIGN (U+3E), and 'hexcode' is inCode represented as a
250 * sequence of hexadecimal digits. The sequence has no leading zeros, and
251 * alphabetic digits are in upper case. The GLC measurement commands treat
252 * the sequence as a single character.
253 */
254 char buf[11];
255 GLint i = 0;
256 GLint n = 0;
257
258 /* Check if a font maps hexadecimal digits */
259 #ifdef _MSC_VER
260 n = sprintf_s(buf, 11, "\\<%X>", (int)inCode);
261 if (n < 0) {
262 __glcRaiseError(GLC_RESOURCE_ERROR);
263 return NULL;
264 }
265 #else
266 n = snprintf(buf, 11, "\\<%X>", (int)inCode);
267 #endif
268 for (i = 0; i < n; i++) {
269 if (!__glcContextGetFont(inContext, buf[i]))
270 /* The code is not rendered, the previous code is thus left unchanged */
271 return NULL;
272 }
273
274 /* Render the '\<hexcode>' sequence */
275 for (i = 0; i < n; i++) {
276 GLint pos = inIsRTL ? n-i-1 : i;
277
278 font = __glcContextGetFont(inContext, buf[pos]);
279 if (font != inPrevCode->font)
280 inPrevCode->code = 0; /*The font has changed, kerning must be disabled*/
281 ret = inProcessCharFunc(buf[pos], inPrevCode->code, inIsRTL, font,
282 inContext, inProcessCharData, GL_TRUE);
283 inPrevCode->code = buf[pos];
284 inPrevCode->font = font;
285 }
286 return ret;
287 }
288 }
289
290
291
292 /* Store an 4x4 identity matrix in 'm' */
__glcMakeIdentity(GLfloat * m)293 static void __glcMakeIdentity(GLfloat* m)
294 {
295 m[0+4*0] = 1; m[0+4*1] = 0; m[0+4*2] = 0; m[0+4*3] = 0;
296 m[1+4*0] = 0; m[1+4*1] = 1; m[1+4*2] = 0; m[1+4*3] = 0;
297 m[2+4*0] = 0; m[2+4*1] = 0; m[2+4*2] = 1; m[2+4*3] = 0;
298 m[3+4*0] = 0; m[3+4*1] = 0; m[3+4*2] = 0; m[3+4*3] = 1;
299 }
300
301
302
303 /* Invert a 4x4 matrix stored in inMatrix. The result is stored in outMatrix
304 * It uses the Gauss-Jordan elimination method
305 */
__glcInvertMatrix(GLfloat * inMatrix,GLfloat * outMatrix)306 static GLboolean __glcInvertMatrix(GLfloat* inMatrix, GLfloat* outMatrix)
307 {
308 int i, j, k, swap;
309 GLfloat t;
310 GLfloat temp[4][4];
311
312 for (i=0; i<4; i++) {
313 for (j=0; j<4; j++) {
314 temp[i][j] = inMatrix[i*4+j];
315 }
316 }
317 __glcMakeIdentity(outMatrix);
318
319 for (i = 0; i < 4; i++) {
320 /* Look for largest element in column */
321 swap = i;
322 for (j = i + 1; j < 4; j++) {
323 if (fabs(temp[j][i]) > fabs(temp[i][i])) {
324 swap = j;
325 }
326 }
327
328 if (swap != i) {
329 /* Swap rows */
330 for (k = 0; k < 4; k++) {
331 t = temp[i][k];
332 temp[i][k] = temp[swap][k];
333 temp[swap][k] = t;
334
335 t = outMatrix[i*4+k];
336 outMatrix[i*4+k] = outMatrix[swap*4+k];
337 outMatrix[swap*4+k] = t;
338 }
339 }
340
341 if (fabs(temp[i][i]) < GLC_EPSILON) {
342 /* No non-zero pivot. The matrix is singular, which shouldn't
343 * happen. This means the user gave us a bad matrix.
344 */
345 return GL_FALSE;
346 }
347
348 t = temp[i][i];
349 for (k = 0; k < 4; k++) {
350 temp[i][k] /= t;
351 outMatrix[i*4+k] /= t;
352 }
353 for (j = 0; j < 4; j++) {
354 if (j != i) {
355 t = temp[j][i];
356 for (k = 0; k < 4; k++) {
357 temp[j][k] -= temp[i][k]*t;
358 outMatrix[j*4+k] -= outMatrix[i*4+k]*t;
359 }
360 }
361 }
362 }
363 return GL_TRUE;
364 }
365
366
367
368 /* Mutiply two 4x4 matrices, the operands are stored in inMatrix1 and inMatrix2
369 * The result is stored in outMatrix which can be neither inMatrix1 nor
370 * inMatrix2.
371 */
__glcMultMatrices(GLfloat * inMatrix1,GLfloat * inMatrix2,GLfloat * outMatrix)372 static void __glcMultMatrices(GLfloat* inMatrix1, GLfloat* inMatrix2,
373 GLfloat* outMatrix)
374 {
375 int i, j;
376
377 for (i = 0; i < 4; i++) {
378 for (j = 0; j < 4; j++) {
379 outMatrix[i*4+j] =
380 inMatrix1[i*4+0]*inMatrix2[0*4+j] +
381 inMatrix1[i*4+1]*inMatrix2[1*4+j] +
382 inMatrix1[i*4+2]*inMatrix2[2*4+j] +
383 inMatrix1[i*4+3]*inMatrix2[3*4+j];
384 }
385 }
386 }
387
388
389
390 /* Compute an optimal size for the glyph to be rendered on the screen if no
391 * display list is planned to be built.
392 */
__glcGetScale(__GLCcontext * inContext,GLfloat * outTransformMatrix,GLfloat * outScaleX,GLfloat * outScaleY)393 void __glcGetScale(__GLCcontext* inContext, GLfloat* outTransformMatrix,
394 GLfloat* outScaleX, GLfloat* outScaleY)
395 {
396 int i = 0;
397
398 if (inContext->renderState.renderStyle != GLC_BITMAP) {
399 /* Compute the matrix that transforms object space coordinates to viewport
400 * coordinates. If we plan to use object space coordinates, this matrix is
401 * set to identity.
402 */
403 GLfloat projectionMatrix[16];
404 GLfloat modelviewMatrix[16];
405 GLint viewport[4];
406
407 glGetIntegerv(GL_VIEWPORT, viewport);
408 glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
409 glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);
410
411 __glcMultMatrices(modelviewMatrix, projectionMatrix, outTransformMatrix);
412
413 if (!inContext->enableState.glObjects && inContext->enableState.hinting) {
414 GLfloat rs[16], m[16];
415 /* Get the scale factors in each X, Y and Z direction */
416 GLfloat sx = sqrt(outTransformMatrix[0] * outTransformMatrix[0]
417 +outTransformMatrix[1] * outTransformMatrix[1]
418 +outTransformMatrix[2] * outTransformMatrix[2]);
419 GLfloat sy = sqrt(outTransformMatrix[4] * outTransformMatrix[4]
420 +outTransformMatrix[5] * outTransformMatrix[5]
421 +outTransformMatrix[6] * outTransformMatrix[6]);
422 GLfloat sz = sqrt(outTransformMatrix[8] * outTransformMatrix[8]
423 +outTransformMatrix[9] * outTransformMatrix[9]
424 +outTransformMatrix[10] * outTransformMatrix[10]);
425 GLfloat x = 0., y = 0.;
426
427 memset(rs, 0, 16 * sizeof(GLfloat));
428 rs[15] = 1.;
429 for (i = 0; i < 3; i++) {
430 rs[0+4*i] = outTransformMatrix[0+4*i] / sx;
431 rs[1+4*i] = outTransformMatrix[1+4*i] / sy;
432 rs[2+4*i] = outTransformMatrix[2+4*i] / sz;
433 }
434 if (!__glcInvertMatrix(rs, rs)) {
435 *outScaleX = 0.f;
436 *outScaleY = 0.f;
437 return;
438 }
439
440 __glcMultMatrices(rs, outTransformMatrix, m);
441 x = ((m[0] + m[12])/(m[3] + m[15]) - m[12]/m[15]) * viewport[2] * 0.5;
442 y = ((m[1] + m[13])/(m[3] + m[15]) - m[13]/m[15]) * viewport[3] * 0.5;
443 *outScaleX = sqrt(x*x+y*y);
444 x = ((m[4] + m[12])/(m[7] + m[15]) - m[12]/m[15]) * viewport[2] * 0.5;
445 y = ((m[5] + m[13])/(m[7] + m[15]) - m[13]/m[15]) * viewport[3] * 0.5;
446 *outScaleY = sqrt(x*x+y*y);
447 }
448 else {
449 *outScaleX = GLC_POINT_SIZE;
450 *outScaleY = GLC_POINT_SIZE;
451 }
452 }
453 else {
454 GLfloat determinant = 0., norm = 0.;
455 GLfloat *transform = inContext->bitmapMatrix;
456
457 /* Compute the norm of the transformation matrix */
458 for (i = 0; i < 4; i++) {
459 if (fabsf(transform[i]) > norm)
460 norm = fabsf(transform[i]);
461 }
462
463 determinant = transform[0] * transform[3] - transform[1] * transform[2];
464
465 /* If the transformation is degenerated, nothing needs to be rendered */
466 if (fabsf(determinant) < norm * GLC_EPSILON) {
467 *outScaleX = 0.f;
468 *outScaleY = 0.f;
469 return;
470 }
471
472 if (inContext->enableState.hinting) {
473 *outScaleX = sqrt(transform[0]*transform[0]+transform[1]*transform[1]);
474 *outScaleY = sqrt(transform[2]*transform[2]+transform[3]*transform[3]);
475 }
476 else {
477 *outScaleX = GLC_POINT_SIZE;
478 *outScaleY = GLC_POINT_SIZE;
479 }
480 }
481 }
482
483
484
485 /* Save the GL State in a structure */
__glcSaveGLState(__GLCglState * inGLState,__GLCcontext * inContext,GLboolean inAll)486 void __glcSaveGLState(__GLCglState* inGLState, __GLCcontext* inContext,
487 GLboolean inAll)
488 {
489 if (inAll || inContext->renderState.renderStyle == GLC_TEXTURE) {
490 inGLState->blend = glIsEnabled(GL_BLEND);
491 glGetIntegerv(GL_BLEND_SRC, &inGLState->blendSrc);
492 glGetIntegerv(GL_BLEND_DST, &inGLState->blendDst);
493 glGetIntegerv(GL_TEXTURE_BINDING_2D, &inGLState->textureID);
494 glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
495 &inGLState->textureEnvMode);
496 if ((!inContext->enableState.glObjects) && GLEW_ARB_pixel_buffer_object)
497 glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING_ARB,
498 &inGLState->bufferObjectID);
499 }
500
501 if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object)
502 glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &inGLState->bufferObjectID);
503 }
504
505
506
507 /* Restore the GL State from a structure */
__glcRestoreGLState(__GLCglState * inGLState,__GLCcontext * inContext,GLboolean inAll)508 void __glcRestoreGLState(__GLCglState* inGLState, __GLCcontext* inContext,
509 GLboolean inAll)
510 {
511 if (inAll || inContext->renderState.renderStyle == GLC_TEXTURE) {
512 if (!inGLState->blend)
513 glDisable(GL_BLEND);
514 glBlendFunc(inGLState->blendSrc, inGLState->blendDst);
515 glBindTexture(GL_TEXTURE_2D, inGLState->textureID);
516 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, inGLState->textureEnvMode);
517 if ((!inContext->enableState.glObjects) && GLEW_ARB_pixel_buffer_object)
518 glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, inGLState->bufferObjectID);
519 }
520
521 if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object)
522 glBindBufferARB(GL_ARRAY_BUFFER_ARB, inGLState->bufferObjectID);
523 }
524
525
526
527 /* Function for GLEW so that it can get a context */
glewGetContext(void)528 GLEWAPI GLEWContext* glewGetContext(void)
529 {
530 __GLCcontext* ctx = GLC_GET_CURRENT_CONTEXT();
531
532 if (!ctx) {
533 __glcRaiseError(GLC_STATE_ERROR);
534 return NULL;
535 }
536
537 return &ctx->glewContext;
538 }
539
540
541
542 /* This function initializes the thread management of QuesoGLC when TLS is not
543 * available. It must be called once (see the macro GLC_INIT_THREAD)
544 */
545 #ifndef HAVE_TLS
__glcInitThread(void)546 void __glcInitThread(void) {
547 #ifdef __WIN32__
548 __glcCommonArea.threadID = GetCurrentThreadId();
549 #else
550 __glcCommonArea.threadID = pthread_self();
551 #endif /* __WIN32__ */
552 }
553 #endif /* HAVE_TLS */
554