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