Integrate QuesoGLC.
Since no fixed release is out at the moment, always use ours. Closes #2828.master
parent
95d09c169b
commit
9cb0afa9b8
|
@ -0,0 +1,330 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#if !defined(__glc_h_)
|
||||
#define __glc_h_
|
||||
|
||||
/************************************************************************
|
||||
* Begin system-specific stuff
|
||||
* from Mesa 3-D graphics library
|
||||
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
/* __WIN32__ */
|
||||
#if !defined(__WIN32__) && (defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__))
|
||||
# define __WIN32__
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
/* GLCAPI, part 1 (use WINGDIAPI, if defined) */
|
||||
#if defined(__WIN32__) && defined(WINGDIAPI) && !defined(GLCAPI)
|
||||
# define GLCAPI WINGDIAPI
|
||||
#endif
|
||||
|
||||
/* GLCAPI, part 2 */
|
||||
#if !defined(GLCAPI)
|
||||
# if defined(_MSC_VER) /* Microsoft Visual C++ */
|
||||
# define GLCAPI __declspec(dllimport)
|
||||
# elif defined(__LCC__) && defined(__WIN32__) /* LCC-Win32 */
|
||||
# define GLCAPI __stdcall
|
||||
# elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define GLCAPI extern __attribute__ ((visibility("default")))
|
||||
# else /* Others (e.g. MinGW, Cygwin, non-win32) */
|
||||
# define GLCAPI extern
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* APIENTRY */
|
||||
#if !defined(APIENTRY)
|
||||
# if defined(__WIN32__)
|
||||
# define APIENTRY __stdcall
|
||||
# else
|
||||
# define APIENTRY
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* CALLBACK */
|
||||
#if !defined(CALLBACK)
|
||||
# if defined(__WIN32__)
|
||||
# define CALLBACK __stdcall
|
||||
# else
|
||||
# define CALLBACK
|
||||
# endif
|
||||
#endif
|
||||
/*
|
||||
* End system-specific stuff.
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
#if defined __APPLE__ && defined __MACH__
|
||||
#include <OpenGL/gl.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void GLCchar;
|
||||
typedef GLint GLCenum;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
typedef GLboolean (CALLBACK *GLCfunc)(GLint);
|
||||
#else
|
||||
typedef GLboolean (CALLBACK *GLCfunc)(GLint);
|
||||
#endif
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
#define GLC_NONE 0x0000
|
||||
|
||||
#define GLC_AUTO_FONT 0x0010
|
||||
#define GLC_GL_OBJECTS 0x0011
|
||||
#define GLC_MIPMAP 0x0012
|
||||
|
||||
#define GLC_OP_glcUnmappedCode 0x0020
|
||||
|
||||
#define GLC_BASELINE 0x0030
|
||||
#define GLC_BOUNDS 0x0031
|
||||
|
||||
#define GLC_PARAMETER_ERROR 0x0040
|
||||
#define GLC_RESOURCE_ERROR 0x0041
|
||||
#define GLC_STATE_ERROR 0x0042
|
||||
|
||||
#define GLC_CHAR_LIST 0x0050
|
||||
#define GLC_FACE_LIST 0x0051
|
||||
|
||||
#define GLC_FAMILY 0x0060
|
||||
#define GLC_MASTER_FORMAT 0x0061
|
||||
#define GLC_VENDOR 0x0062
|
||||
#define GLC_VERSION 0x0063
|
||||
|
||||
#define GLC_CHAR_COUNT 0x0070
|
||||
#define GLC_FACE_COUNT 0x0071
|
||||
#define GLC_IS_FIXED_PITCH 0x0072
|
||||
#define GLC_MAX_MAPPED_CODE 0x0073
|
||||
#define GLC_MIN_MAPPED_CODE 0x0074
|
||||
#define GLC_IS_OUTLINE 0x0075
|
||||
|
||||
#define GLC_CATALOG_LIST 0x0080
|
||||
|
||||
#define GLC_CURRENT_FONT_LIST 0x0090
|
||||
#define GLC_FONT_LIST 0x0091
|
||||
#define GLC_LIST_OBJECT_LIST 0x0092
|
||||
#define GLC_TEXTURE_OBJECT_LIST 0x0093
|
||||
|
||||
#define GLC_DATA_POINTER 0x00A0
|
||||
|
||||
#define GLC_EXTENSIONS 0x00B0
|
||||
#define GLC_RELEASE 0x00B1
|
||||
|
||||
#define GLC_RESOLUTION 0x00C0
|
||||
|
||||
#define GLC_BITMAP_MATRIX 0x00D0
|
||||
|
||||
#define GLC_CATALOG_COUNT 0x00E0
|
||||
#define GLC_CURRENT_FONT_COUNT 0x00E1
|
||||
#define GLC_FONT_COUNT 0x00E2
|
||||
#define GLC_LIST_OBJECT_COUNT 0x00E3
|
||||
#define GLC_MASTER_COUNT 0x00E4
|
||||
#define GLC_MEASURED_CHAR_COUNT 0x00E5
|
||||
#define GLC_RENDER_STYLE 0x00E6
|
||||
#define GLC_REPLACEMENT_CODE 0x00E7
|
||||
#define GLC_STRING_TYPE 0x00E8
|
||||
#define GLC_TEXTURE_OBJECT_COUNT 0x00E9
|
||||
#define GLC_VERSION_MAJOR 0x00EA
|
||||
#define GLC_VERSION_MINOR 0x00EB
|
||||
|
||||
#define GLC_BITMAP 0x0100
|
||||
#define GLC_LINE 0x0101
|
||||
#define GLC_TEXTURE 0x0102
|
||||
#define GLC_TRIANGLE 0x0103
|
||||
|
||||
#define GLC_UCS1 0x0110
|
||||
#define GLC_UCS2 0x0111
|
||||
#define GLC_UCS4 0x0112
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
GLCAPI void APIENTRY glcContext (GLint inContext);
|
||||
GLCAPI void APIENTRY glcDeleteContext (GLint inContext);
|
||||
GLCAPI GLint APIENTRY glcGenContext (void);
|
||||
GLCAPI GLint* APIENTRY glcGetAllContexts (void);
|
||||
GLCAPI GLint APIENTRY glcGetCurrentContext (void);
|
||||
GLCAPI GLCenum APIENTRY glcGetError (void);
|
||||
GLCAPI GLboolean APIENTRY glcIsContext (GLint inContext);
|
||||
|
||||
GLCAPI void APIENTRY glcCallbackFunc (GLCenum inOpcode, GLCfunc inFunc);
|
||||
GLCAPI void APIENTRY glcDataPointer (GLvoid *inPointer);
|
||||
GLCAPI void APIENTRY glcDeleteGLObjects (void);
|
||||
GLCAPI void APIENTRY glcDisable (GLCenum inAttrib);
|
||||
GLCAPI void APIENTRY glcEnable (GLCenum inAttrib);
|
||||
GLCAPI GLCfunc APIENTRY glcGetCallbackFunc (GLCenum inOpcode);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetListc (GLCenum inAttrib, GLint inIndex);
|
||||
GLCAPI GLint APIENTRY glcGetListi (GLCenum inAttrib, GLint inIndex);
|
||||
GLCAPI GLvoid* APIENTRY glcGetPointer (GLCenum inAttrib);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetc (GLCenum inAttrib);
|
||||
GLCAPI GLfloat APIENTRY glcGetf (GLCenum inAttrib);
|
||||
GLCAPI GLfloat* APIENTRY glcGetfv (GLCenum inAttrib, GLfloat *outVec);
|
||||
GLCAPI GLint APIENTRY glcGeti (GLCenum inAttrib);
|
||||
GLCAPI GLboolean APIENTRY glcIsEnabled (GLCenum inAttrib);
|
||||
GLCAPI void APIENTRY glcStringType (GLCenum inStringType);
|
||||
|
||||
GLCAPI void APIENTRY glcAppendCatalog (const GLCchar *inCatalog);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetMasterListc (GLint inMaster,
|
||||
GLCenum inAttrib,
|
||||
GLint inIndex);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetMasterMap (GLint inMaster, GLint inCode);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetMasterc (GLint inMaster, GLCenum inAttrib);
|
||||
GLCAPI GLint APIENTRY glcGetMasteri (GLint inMaster, GLCenum inAttrib);
|
||||
GLCAPI void APIENTRY glcPrependCatalog (const GLCchar *inCatalog);
|
||||
GLCAPI void APIENTRY glcRemoveCatalog (GLint inIndex);
|
||||
|
||||
GLCAPI void APIENTRY glcAppendFont (GLint inFont);
|
||||
GLCAPI void APIENTRY glcDeleteFont (GLint inFont);
|
||||
GLCAPI void APIENTRY glcFont (GLint inFont);
|
||||
GLCAPI GLboolean APIENTRY glcFontFace (GLint inFont, const GLCchar *inFace);
|
||||
GLCAPI void APIENTRY glcFontMap (GLint inFont, GLint inCode,
|
||||
const GLCchar *inCharName);
|
||||
GLCAPI GLint APIENTRY glcGenFontID (void);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetFontFace (GLint inFont);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetFontListc (GLint inFont,
|
||||
GLCenum inAttrib,
|
||||
GLint inIndex);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetFontMap (GLint inFont, GLint inCode);
|
||||
GLCAPI const GLbyte* APIENTRY glcGetFontMasterArray (GLint inFont,
|
||||
GLboolean inFull,
|
||||
GLint *outCount);
|
||||
GLCAPI const GLCchar* APIENTRY glcGetFontc (GLint inFont, GLCenum inAttrib);
|
||||
GLCAPI GLint APIENTRY glcGetFonti (GLint inFont, GLCenum inAttrib);
|
||||
GLCAPI GLboolean APIENTRY glcIsFont (GLint inFont);
|
||||
GLCAPI GLint APIENTRY glcNewFontFromFamily (GLint inFont,
|
||||
const GLCchar *inFamily);
|
||||
GLCAPI GLint APIENTRY glcNewFontFromMaster (GLint inFont, GLint inMaster);
|
||||
|
||||
GLCAPI void APIENTRY glcLoadIdentity (void);
|
||||
GLCAPI void APIENTRY glcLoadMatrix (const GLfloat *inMatrix);
|
||||
GLCAPI void APIENTRY glcMultMatrix (const GLfloat *inMatrix);
|
||||
GLCAPI void APIENTRY glcRotate (GLfloat inAngle);
|
||||
GLCAPI void APIENTRY glcScale (GLfloat inX, GLfloat inY);
|
||||
|
||||
GLCAPI void APIENTRY glcRenderChar (GLint inCode);
|
||||
GLCAPI void APIENTRY glcRenderCountedString (GLint inCount,
|
||||
const GLCchar *inString);
|
||||
GLCAPI void APIENTRY glcRenderString (const GLCchar *inString);
|
||||
GLCAPI void APIENTRY glcRenderStyle (GLCenum inStyle);
|
||||
GLCAPI void APIENTRY glcReplacementCode (GLint inCode);
|
||||
GLCAPI void APIENTRY glcResolution (GLfloat inVal);
|
||||
|
||||
GLCAPI GLfloat* APIENTRY glcGetCharMetric (GLint inCode, GLCenum inMetric,
|
||||
GLfloat *outVec);
|
||||
GLCAPI GLfloat* APIENTRY glcGetMaxCharMetric (GLCenum inMetric,
|
||||
GLfloat *outVec);
|
||||
GLCAPI GLfloat* APIENTRY glcGetStringCharMetric (GLint inIndex,
|
||||
GLCenum inMetric,
|
||||
GLfloat *outVec);
|
||||
GLCAPI GLfloat* APIENTRY glcGetStringMetric (GLCenum inMetric, GLfloat *outVec);
|
||||
GLCAPI GLint APIENTRY glcMeasureCountedString (GLboolean inMeasureChars,
|
||||
GLint inCount,
|
||||
const GLCchar *inString);
|
||||
GLCAPI GLint APIENTRY glcMeasureString (GLboolean inMeasureChars,
|
||||
const GLCchar *inString);
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
#define GLC_SGI_ufm_typeface_handle 1
|
||||
#define GLC_UFM_TYPEFACE_HANDLE_SGI 0x8001
|
||||
#define GLC_UFM_TYPEFACE_HANDLE_COUNT_SGI 0x8003
|
||||
GLCAPI GLint APIENTRY glcGetMasterListiSGI(GLint inMaster, GLCenum inAttrib,
|
||||
GLint inIndex);
|
||||
GLCAPI GLint APIENTRY glcGetFontListiSGI(GLint inFont, GLCenum inAttrib,
|
||||
GLint inIndex);
|
||||
|
||||
#define GLC_SGI_full_name 1
|
||||
#define GLC_FULL_NAME_SGI 0x8002
|
||||
|
||||
#define GLC_QSO_utf8 1
|
||||
#define GLC_UTF8_QSO 0x8004
|
||||
|
||||
#define GLC_QSO_hinting 1
|
||||
#define GLC_HINTING_QSO 0x8005
|
||||
|
||||
#define GLC_QSO_extrude 1
|
||||
#define GLC_EXTRUDE_QSO 0x8006
|
||||
|
||||
#define GLC_QSO_kerning 1
|
||||
#define GLC_KERNING_QSO 0x8007
|
||||
|
||||
#define GLC_QSO_matrix_stack 1
|
||||
#define GLC_MATRIX_STACK_DEPTH_QSO 0x8008
|
||||
#define GLC_MAX_MATRIX_STACK_DEPTH_QSO 0x8009
|
||||
#define GLC_STACK_OVERFLOW_QSO 0x800A
|
||||
#define GLC_STACK_UNDERFLOW_QSO 0x800B
|
||||
GLCAPI void APIENTRY glcPushMatrixQSO(void);
|
||||
GLCAPI void APIENTRY glcPopMatrixQSO(void);
|
||||
|
||||
#define GLC_QSO_attrib_stack 1
|
||||
#define GLC_ENABLE_BIT_QSO 0x00000001
|
||||
#define GLC_RENDER_BIT_QSO 0x00000002
|
||||
#define GLC_STRING_BIT_QSO 0x00000004
|
||||
#define GLC_GL_ATTRIB_BIT_QSO 0x00000008
|
||||
#define GLC_ALL_ATTRIB_BITS_QSO 0x0000FFFF
|
||||
#define GLC_ATTRIB_STACK_DEPTH_QSO 0x800C
|
||||
#define GLC_MAX_ATTRIB_STACK_DEPTH_QSO 0x800D
|
||||
GLCAPI void APIENTRY glcPushAttribQSO(GLbitfield inMask);
|
||||
GLCAPI void APIENTRY glcPopAttribQSO(void);
|
||||
|
||||
#define GLC_QSO_buffer_object 1
|
||||
#define GLC_BUFFER_OBJECT_COUNT_QSO 0x800E
|
||||
#define GLC_BUFFER_OBJECT_LIST_QSO 0x800F
|
||||
|
||||
#define GLC_QSO_render_parameter 1
|
||||
#define GLC_PARAMETRIC_TOLERANCE_QSO 0x8010
|
||||
GLCAPI void APIENTRY glcRenderParameteriQSO(GLenum inAttrib, GLint inVal);
|
||||
GLCAPI void APIENTRY glcRenderParameterfQSO(GLenum inAttrib, GLfloat inVal);
|
||||
|
||||
#define GLC_QSO_render_pixmap
|
||||
#define GLC_PIXMAP_QSO 0x8011
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* defined (__glc_h_) */
|
|
@ -0,0 +1,50 @@
|
|||
AM_CPPFLAGS = $(FREETYPE_CFLAGS) $(FRIBIDI_CFLAGS) $(FONTCONFIG_CFLAGS) $(WZ_CPPFLAGS) $(OPENGL_CFLAGS) $(GLEW_CFLAGS)
|
||||
AM_CFLAGS =
|
||||
AM_CXXFLAGS =
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
WZ_CPPFLAGS += -I`pwd`
|
||||
WZ_CFLAGS += -I`pwd`
|
||||
WZ_CXXFLAGS += -I`pwd`
|
||||
|
||||
noinst_LIBRARIES = libquesoglc.a
|
||||
noinst_HEADERS = \
|
||||
GL/glc.h \
|
||||
except.h \
|
||||
internal.h \
|
||||
oarray.h \
|
||||
ocharmap.h \
|
||||
ocontext.h \
|
||||
ofacedesc.h \
|
||||
ofont.h \
|
||||
oglyph.h \
|
||||
omaster.h \
|
||||
qglc_config.h \
|
||||
texture.h
|
||||
|
||||
libquesoglc_a_SOURCES = \
|
||||
database.c \
|
||||
context.c \
|
||||
except.c \
|
||||
font.c \
|
||||
global.c \
|
||||
master.c \
|
||||
measure.c \
|
||||
misc.c \
|
||||
oarray.c \
|
||||
ofacedesc.c \
|
||||
ofont.c \
|
||||
oglyph.c \
|
||||
render.c \
|
||||
scalable.c \
|
||||
texture.c \
|
||||
transform.c \
|
||||
unicode.c
|
||||
|
||||
|
||||
if MINGW32
|
||||
libquesoglc_a_SOURCES += win32-ocharmap.c win32-ocontext.c win32-omaster.c
|
||||
else
|
||||
libquesoglc_a_SOURCES += ocharmap.c ocontext.c omaster.c
|
||||
endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,586 @@
|
|||
/* This is automagically generated by buildDB.py */
|
||||
#include "internal.h"
|
||||
|
||||
const __GLCdataCodeFromName __glcCodeFromNameArray[] = {
|
||||
{ 180, "ACUTE ACCENT"},
|
||||
{ 38, "AMPERSAND"},
|
||||
{ 39, "APOSTROPHE"},
|
||||
{ 42, "ASTERISK"},
|
||||
{ 166, "BROKEN BAR"},
|
||||
{ 184, "CEDILLA"},
|
||||
{ 162, "CENT SIGN"},
|
||||
{ 94, "CIRCUMFLEX ACCENT"},
|
||||
{ 58, "COLON"},
|
||||
{ 44, "COMMA"},
|
||||
{ 64, "COMMERCIAL AT"},
|
||||
{ 169, "COPYRIGHT SIGN"},
|
||||
{ 164, "CURRENCY SIGN"},
|
||||
{ 176, "DEGREE SIGN"},
|
||||
{ 168, "DIAERESIS"},
|
||||
{ 56, "DIGIT EIGHT"},
|
||||
{ 53, "DIGIT FIVE"},
|
||||
{ 52, "DIGIT FOUR"},
|
||||
{ 57, "DIGIT NINE"},
|
||||
{ 49, "DIGIT ONE"},
|
||||
{ 55, "DIGIT SEVEN"},
|
||||
{ 54, "DIGIT SIX"},
|
||||
{ 51, "DIGIT THREE"},
|
||||
{ 50, "DIGIT TWO"},
|
||||
{ 48, "DIGIT ZERO"},
|
||||
{ 247, "DIVISION SIGN"},
|
||||
{ 36, "DOLLAR SIGN"},
|
||||
{ 61, "EQUALS SIGN"},
|
||||
{ 33, "EXCLAMATION MARK"},
|
||||
{ 170, "FEMININE ORDINAL INDICATOR"},
|
||||
{ 46, "FULL STOP"},
|
||||
{ 96, "GRAVE ACCENT"},
|
||||
{ 62, "GREATER-THAN SIGN"},
|
||||
{ 45, "HYPHEN-MINUS"},
|
||||
{ 161, "INVERTED EXCLAMATION MARK"},
|
||||
{ 191, "INVERTED QUESTION MARK"},
|
||||
{ 65, "LATIN CAPITAL LETTER A"},
|
||||
{ 193, "LATIN CAPITAL LETTER A WITH ACUTE"},
|
||||
{ 258, "LATIN CAPITAL LETTER A WITH BREVE"},
|
||||
{ 194, "LATIN CAPITAL LETTER A WITH CIRCUMFLEX"},
|
||||
{ 196, "LATIN CAPITAL LETTER A WITH DIAERESIS"},
|
||||
{ 192, "LATIN CAPITAL LETTER A WITH GRAVE"},
|
||||
{ 256, "LATIN CAPITAL LETTER A WITH MACRON"},
|
||||
{ 260, "LATIN CAPITAL LETTER A WITH OGONEK"},
|
||||
{ 197, "LATIN CAPITAL LETTER A WITH RING ABOVE"},
|
||||
{ 195, "LATIN CAPITAL LETTER A WITH TILDE"},
|
||||
{ 198, "LATIN CAPITAL LETTER AE"},
|
||||
{ 66, "LATIN CAPITAL LETTER B"},
|
||||
{ 67, "LATIN CAPITAL LETTER C"},
|
||||
{ 262, "LATIN CAPITAL LETTER C WITH ACUTE"},
|
||||
{ 268, "LATIN CAPITAL LETTER C WITH CARON"},
|
||||
{ 199, "LATIN CAPITAL LETTER C WITH CEDILLA"},
|
||||
{ 264, "LATIN CAPITAL LETTER C WITH CIRCUMFLEX"},
|
||||
{ 266, "LATIN CAPITAL LETTER C WITH DOT ABOVE"},
|
||||
{ 68, "LATIN CAPITAL LETTER D"},
|
||||
{ 270, "LATIN CAPITAL LETTER D WITH CARON"},
|
||||
{ 272, "LATIN CAPITAL LETTER D WITH STROKE"},
|
||||
{ 69, "LATIN CAPITAL LETTER E"},
|
||||
{ 201, "LATIN CAPITAL LETTER E WITH ACUTE"},
|
||||
{ 276, "LATIN CAPITAL LETTER E WITH BREVE"},
|
||||
{ 282, "LATIN CAPITAL LETTER E WITH CARON"},
|
||||
{ 202, "LATIN CAPITAL LETTER E WITH CIRCUMFLEX"},
|
||||
{ 203, "LATIN CAPITAL LETTER E WITH DIAERESIS"},
|
||||
{ 278, "LATIN CAPITAL LETTER E WITH DOT ABOVE"},
|
||||
{ 200, "LATIN CAPITAL LETTER E WITH GRAVE"},
|
||||
{ 274, "LATIN CAPITAL LETTER E WITH MACRON"},
|
||||
{ 280, "LATIN CAPITAL LETTER E WITH OGONEK"},
|
||||
{ 208, "LATIN CAPITAL LETTER ETH"},
|
||||
{ 70, "LATIN CAPITAL LETTER F"},
|
||||
{ 71, "LATIN CAPITAL LETTER G"},
|
||||
{ 286, "LATIN CAPITAL LETTER G WITH BREVE"},
|
||||
{ 290, "LATIN CAPITAL LETTER G WITH CEDILLA"},
|
||||
{ 284, "LATIN CAPITAL LETTER G WITH CIRCUMFLEX"},
|
||||
{ 288, "LATIN CAPITAL LETTER G WITH DOT ABOVE"},
|
||||
{ 72, "LATIN CAPITAL LETTER H"},
|
||||
{ 292, "LATIN CAPITAL LETTER H WITH CIRCUMFLEX"},
|
||||
{ 294, "LATIN CAPITAL LETTER H WITH STROKE"},
|
||||
{ 73, "LATIN CAPITAL LETTER I"},
|
||||
{ 205, "LATIN CAPITAL LETTER I WITH ACUTE"},
|
||||
{ 300, "LATIN CAPITAL LETTER I WITH BREVE"},
|
||||
{ 206, "LATIN CAPITAL LETTER I WITH CIRCUMFLEX"},
|
||||
{ 207, "LATIN CAPITAL LETTER I WITH DIAERESIS"},
|
||||
{ 304, "LATIN CAPITAL LETTER I WITH DOT ABOVE"},
|
||||
{ 204, "LATIN CAPITAL LETTER I WITH GRAVE"},
|
||||
{ 298, "LATIN CAPITAL LETTER I WITH MACRON"},
|
||||
{ 302, "LATIN CAPITAL LETTER I WITH OGONEK"},
|
||||
{ 296, "LATIN CAPITAL LETTER I WITH TILDE"},
|
||||
{ 74, "LATIN CAPITAL LETTER J"},
|
||||
{ 308, "LATIN CAPITAL LETTER J WITH CIRCUMFLEX"},
|
||||
{ 75, "LATIN CAPITAL LETTER K"},
|
||||
{ 310, "LATIN CAPITAL LETTER K WITH CEDILLA"},
|
||||
{ 76, "LATIN CAPITAL LETTER L"},
|
||||
{ 313, "LATIN CAPITAL LETTER L WITH ACUTE"},
|
||||
{ 317, "LATIN CAPITAL LETTER L WITH CARON"},
|
||||
{ 315, "LATIN CAPITAL LETTER L WITH CEDILLA"},
|
||||
{ 319, "LATIN CAPITAL LETTER L WITH MIDDLE DOT"},
|
||||
{ 77, "LATIN CAPITAL LETTER M"},
|
||||
{ 78, "LATIN CAPITAL LETTER N"},
|
||||
{ 209, "LATIN CAPITAL LETTER N WITH TILDE"},
|
||||
{ 79, "LATIN CAPITAL LETTER O"},
|
||||
{ 211, "LATIN CAPITAL LETTER O WITH ACUTE"},
|
||||
{ 212, "LATIN CAPITAL LETTER O WITH CIRCUMFLEX"},
|
||||
{ 214, "LATIN CAPITAL LETTER O WITH DIAERESIS"},
|
||||
{ 210, "LATIN CAPITAL LETTER O WITH GRAVE"},
|
||||
{ 216, "LATIN CAPITAL LETTER O WITH STROKE"},
|
||||
{ 213, "LATIN CAPITAL LETTER O WITH TILDE"},
|
||||
{ 80, "LATIN CAPITAL LETTER P"},
|
||||
{ 81, "LATIN CAPITAL LETTER Q"},
|
||||
{ 82, "LATIN CAPITAL LETTER R"},
|
||||
{ 83, "LATIN CAPITAL LETTER S"},
|
||||
{ 84, "LATIN CAPITAL LETTER T"},
|
||||
{ 222, "LATIN CAPITAL LETTER THORN"},
|
||||
{ 85, "LATIN CAPITAL LETTER U"},
|
||||
{ 218, "LATIN CAPITAL LETTER U WITH ACUTE"},
|
||||
{ 219, "LATIN CAPITAL LETTER U WITH CIRCUMFLEX"},
|
||||
{ 220, "LATIN CAPITAL LETTER U WITH DIAERESIS"},
|
||||
{ 217, "LATIN CAPITAL LETTER U WITH GRAVE"},
|
||||
{ 86, "LATIN CAPITAL LETTER V"},
|
||||
{ 87, "LATIN CAPITAL LETTER W"},
|
||||
{ 88, "LATIN CAPITAL LETTER X"},
|
||||
{ 89, "LATIN CAPITAL LETTER Y"},
|
||||
{ 221, "LATIN CAPITAL LETTER Y WITH ACUTE"},
|
||||
{ 90, "LATIN CAPITAL LETTER Z"},
|
||||
{ 306, "LATIN CAPITAL LIGATURE IJ"},
|
||||
{ 97, "LATIN SMALL LETTER A"},
|
||||
{ 225, "LATIN SMALL LETTER A WITH ACUTE"},
|
||||
{ 259, "LATIN SMALL LETTER A WITH BREVE"},
|
||||
{ 226, "LATIN SMALL LETTER A WITH CIRCUMFLEX"},
|
||||
{ 228, "LATIN SMALL LETTER A WITH DIAERESIS"},
|
||||
{ 224, "LATIN SMALL LETTER A WITH GRAVE"},
|
||||
{ 257, "LATIN SMALL LETTER A WITH MACRON"},
|
||||
{ 261, "LATIN SMALL LETTER A WITH OGONEK"},
|
||||
{ 229, "LATIN SMALL LETTER A WITH RING ABOVE"},
|
||||
{ 227, "LATIN SMALL LETTER A WITH TILDE"},
|
||||
{ 230, "LATIN SMALL LETTER AE"},
|
||||
{ 98, "LATIN SMALL LETTER B"},
|
||||
{ 99, "LATIN SMALL LETTER C"},
|
||||
{ 263, "LATIN SMALL LETTER C WITH ACUTE"},
|
||||
{ 269, "LATIN SMALL LETTER C WITH CARON"},
|
||||
{ 231, "LATIN SMALL LETTER C WITH CEDILLA"},
|
||||
{ 265, "LATIN SMALL LETTER C WITH CIRCUMFLEX"},
|
||||
{ 267, "LATIN SMALL LETTER C WITH DOT ABOVE"},
|
||||
{ 100, "LATIN SMALL LETTER D"},
|
||||
{ 271, "LATIN SMALL LETTER D WITH CARON"},
|
||||
{ 273, "LATIN SMALL LETTER D WITH STROKE"},
|
||||
{ 305, "LATIN SMALL LETTER DOTLESS I"},
|
||||
{ 101, "LATIN SMALL LETTER E"},
|
||||
{ 233, "LATIN SMALL LETTER E WITH ACUTE"},
|
||||
{ 277, "LATIN SMALL LETTER E WITH BREVE"},
|
||||
{ 283, "LATIN SMALL LETTER E WITH CARON"},
|
||||
{ 234, "LATIN SMALL LETTER E WITH CIRCUMFLEX"},
|
||||
{ 235, "LATIN SMALL LETTER E WITH DIAERESIS"},
|
||||
{ 279, "LATIN SMALL LETTER E WITH DOT ABOVE"},
|
||||
{ 232, "LATIN SMALL LETTER E WITH GRAVE"},
|
||||
{ 275, "LATIN SMALL LETTER E WITH MACRON"},
|
||||
{ 281, "LATIN SMALL LETTER E WITH OGONEK"},
|
||||
{ 240, "LATIN SMALL LETTER ETH"},
|
||||
{ 102, "LATIN SMALL LETTER F"},
|
||||
{ 103, "LATIN SMALL LETTER G"},
|
||||
{ 287, "LATIN SMALL LETTER G WITH BREVE"},
|
||||
{ 291, "LATIN SMALL LETTER G WITH CEDILLA"},
|
||||
{ 285, "LATIN SMALL LETTER G WITH CIRCUMFLEX"},
|
||||
{ 289, "LATIN SMALL LETTER G WITH DOT ABOVE"},
|
||||
{ 104, "LATIN SMALL LETTER H"},
|
||||
{ 293, "LATIN SMALL LETTER H WITH CIRCUMFLEX"},
|
||||
{ 295, "LATIN SMALL LETTER H WITH STROKE"},
|
||||
{ 105, "LATIN SMALL LETTER I"},
|
||||
{ 237, "LATIN SMALL LETTER I WITH ACUTE"},
|
||||
{ 301, "LATIN SMALL LETTER I WITH BREVE"},
|
||||
{ 238, "LATIN SMALL LETTER I WITH CIRCUMFLEX"},
|
||||
{ 239, "LATIN SMALL LETTER I WITH DIAERESIS"},
|
||||
{ 236, "LATIN SMALL LETTER I WITH GRAVE"},
|
||||
{ 299, "LATIN SMALL LETTER I WITH MACRON"},
|
||||
{ 303, "LATIN SMALL LETTER I WITH OGONEK"},
|
||||
{ 297, "LATIN SMALL LETTER I WITH TILDE"},
|
||||
{ 106, "LATIN SMALL LETTER J"},
|
||||
{ 309, "LATIN SMALL LETTER J WITH CIRCUMFLEX"},
|
||||
{ 107, "LATIN SMALL LETTER K"},
|
||||
{ 311, "LATIN SMALL LETTER K WITH CEDILLA"},
|
||||
{ 312, "LATIN SMALL LETTER KRA"},
|
||||
{ 108, "LATIN SMALL LETTER L"},
|
||||
{ 314, "LATIN SMALL LETTER L WITH ACUTE"},
|
||||
{ 318, "LATIN SMALL LETTER L WITH CARON"},
|
||||
{ 316, "LATIN SMALL LETTER L WITH CEDILLA"},
|
||||
{ 320, "LATIN SMALL LETTER L WITH MIDDLE DOT"},
|
||||
{ 109, "LATIN SMALL LETTER M"},
|
||||
{ 110, "LATIN SMALL LETTER N"},
|
||||
{ 241, "LATIN SMALL LETTER N WITH TILDE"},
|
||||
{ 111, "LATIN SMALL LETTER O"},
|
||||
{ 243, "LATIN SMALL LETTER O WITH ACUTE"},
|
||||
{ 244, "LATIN SMALL LETTER O WITH CIRCUMFLEX"},
|
||||
{ 246, "LATIN SMALL LETTER O WITH DIAERESIS"},
|
||||
{ 242, "LATIN SMALL LETTER O WITH GRAVE"},
|
||||
{ 248, "LATIN SMALL LETTER O WITH STROKE"},
|
||||
{ 245, "LATIN SMALL LETTER O WITH TILDE"},
|
||||
{ 112, "LATIN SMALL LETTER P"},
|
||||
{ 113, "LATIN SMALL LETTER Q"},
|
||||
{ 114, "LATIN SMALL LETTER R"},
|
||||
{ 115, "LATIN SMALL LETTER S"},
|
||||
{ 223, "LATIN SMALL LETTER SHARP S"},
|
||||
{ 116, "LATIN SMALL LETTER T"},
|
||||
{ 254, "LATIN SMALL LETTER THORN"},
|
||||
{ 117, "LATIN SMALL LETTER U"},
|
||||
{ 250, "LATIN SMALL LETTER U WITH ACUTE"},
|
||||
{ 251, "LATIN SMALL LETTER U WITH CIRCUMFLEX"},
|
||||
{ 252, "LATIN SMALL LETTER U WITH DIAERESIS"},
|
||||
{ 249, "LATIN SMALL LETTER U WITH GRAVE"},
|
||||
{ 118, "LATIN SMALL LETTER V"},
|
||||
{ 119, "LATIN SMALL LETTER W"},
|
||||
{ 120, "LATIN SMALL LETTER X"},
|
||||
{ 121, "LATIN SMALL LETTER Y"},
|
||||
{ 253, "LATIN SMALL LETTER Y WITH ACUTE"},
|
||||
{ 255, "LATIN SMALL LETTER Y WITH DIAERESIS"},
|
||||
{ 122, "LATIN SMALL LETTER Z"},
|
||||
{ 307, "LATIN SMALL LIGATURE IJ"},
|
||||
{ 123, "LEFT CURLY BRACKET"},
|
||||
{ 40, "LEFT PARENTHESIS"},
|
||||
{ 91, "LEFT SQUARE BRACKET"},
|
||||
{ 171, "LEFT-POINTING DOUBLE ANGLE QUOTATION MARK"},
|
||||
{ 60, "LESS-THAN SIGN"},
|
||||
{ 95, "LOW LINE"},
|
||||
{ 175, "MACRON"},
|
||||
{ 186, "MASCULINE ORDINAL INDICATOR"},
|
||||
{ 181, "MICRO SIGN"},
|
||||
{ 183, "MIDDLE DOT"},
|
||||
{ 215, "MULTIPLICATION SIGN"},
|
||||
{ 160, "NO-BREAK SPACE"},
|
||||
{ 172, "NOT SIGN"},
|
||||
{ 35, "NUMBER SIGN"},
|
||||
{ 37, "PERCENT SIGN"},
|
||||
{ 182, "PILCROW SIGN"},
|
||||
{ 43, "PLUS SIGN"},
|
||||
{ 177, "PLUS-MINUS SIGN"},
|
||||
{ 163, "POUND SIGN"},
|
||||
{ 63, "QUESTION MARK"},
|
||||
{ 34, "QUOTATION MARK"},
|
||||
{ 174, "REGISTERED SIGN"},
|
||||
{ 92, "REVERSE SOLIDUS"},
|
||||
{ 125, "RIGHT CURLY BRACKET"},
|
||||
{ 41, "RIGHT PARENTHESIS"},
|
||||
{ 93, "RIGHT SQUARE BRACKET"},
|
||||
{ 187, "RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK"},
|
||||
{ 167, "SECTION SIGN"},
|
||||
{ 59, "SEMICOLON"},
|
||||
{ 173, "SOFT HYPHEN"},
|
||||
{ 47, "SOLIDUS"},
|
||||
{ 32, "SPACE"},
|
||||
{ 185, "SUPERSCRIPT ONE"},
|
||||
{ 179, "SUPERSCRIPT THREE"},
|
||||
{ 178, "SUPERSCRIPT TWO"},
|
||||
{ 126, "TILDE"},
|
||||
{ 124, "VERTICAL LINE"},
|
||||
{ 189, "VULGAR FRACTION ONE HALF"},
|
||||
{ 188, "VULGAR FRACTION ONE QUARTER"},
|
||||
{ 190, "VULGAR FRACTION THREE QUARTERS"},
|
||||
{ 165, "YEN SIGN"},
|
||||
};
|
||||
const GLint __glcNameFromCodeArray[] = {
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
246,
|
||||
28,
|
||||
235,
|
||||
228,
|
||||
26,
|
||||
229,
|
||||
1,
|
||||
2,
|
||||
216,
|
||||
239,
|
||||
3,
|
||||
231,
|
||||
9,
|
||||
33,
|
||||
30,
|
||||
245,
|
||||
24,
|
||||
19,
|
||||
23,
|
||||
22,
|
||||
17,
|
||||
16,
|
||||
21,
|
||||
20,
|
||||
15,
|
||||
18,
|
||||
8,
|
||||
243,
|
||||
219,
|
||||
27,
|
||||
32,
|
||||
234,
|
||||
10,
|
||||
36,
|
||||
47,
|
||||
48,
|
||||
54,
|
||||
57,
|
||||
68,
|
||||
69,
|
||||
74,
|
||||
77,
|
||||
87,
|
||||
89,
|
||||
91,
|
||||
96,
|
||||
97,
|
||||
99,
|
||||
106,
|
||||
107,
|
||||
108,
|
||||
109,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
118,
|
||||
119,
|
||||
120,
|
||||
122,
|
||||
217,
|
||||
237,
|
||||
240,
|
||||
7,
|
||||
220,
|
||||
31,
|
||||
124,
|
||||
135,
|
||||
136,
|
||||
142,
|
||||
146,
|
||||
157,
|
||||
158,
|
||||
163,
|
||||
166,
|
||||
175,
|
||||
177,
|
||||
180,
|
||||
185,
|
||||
186,
|
||||
188,
|
||||
195,
|
||||
196,
|
||||
197,
|
||||
198,
|
||||
200,
|
||||
202,
|
||||
207,
|
||||
208,
|
||||
209,
|
||||
210,
|
||||
213,
|
||||
215,
|
||||
251,
|
||||
238,
|
||||
250,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
226,
|
||||
34,
|
||||
6,
|
||||
233,
|
||||
12,
|
||||
255,
|
||||
4,
|
||||
242,
|
||||
14,
|
||||
11,
|
||||
29,
|
||||
218,
|
||||
227,
|
||||
244,
|
||||
236,
|
||||
221,
|
||||
13,
|
||||
232,
|
||||
249,
|
||||
248,
|
||||
0,
|
||||
223,
|
||||
230,
|
||||
224,
|
||||
5,
|
||||
247,
|
||||
222,
|
||||
241,
|
||||
253,
|
||||
252,
|
||||
254,
|
||||
35,
|
||||
41,
|
||||
37,
|
||||
39,
|
||||
45,
|
||||
40,
|
||||
44,
|
||||
46,
|
||||
51,
|
||||
64,
|
||||
58,
|
||||
61,
|
||||
62,
|
||||
83,
|
||||
78,
|
||||
80,
|
||||
81,
|
||||
67,
|
||||
98,
|
||||
103,
|
||||
100,
|
||||
101,
|
||||
105,
|
||||
102,
|
||||
225,
|
||||
104,
|
||||
116,
|
||||
113,
|
||||
114,
|
||||
115,
|
||||
121,
|
||||
111,
|
||||
199,
|
||||
129,
|
||||
125,
|
||||
127,
|
||||
133,
|
||||
128,
|
||||
132,
|
||||
134,
|
||||
139,
|
||||
153,
|
||||
147,
|
||||
150,
|
||||
151,
|
||||
171,
|
||||
167,
|
||||
169,
|
||||
170,
|
||||
156,
|
||||
187,
|
||||
192,
|
||||
189,
|
||||
190,
|
||||
194,
|
||||
191,
|
||||
25,
|
||||
193,
|
||||
206,
|
||||
203,
|
||||
204,
|
||||
205,
|
||||
211,
|
||||
201,
|
||||
212,
|
||||
42,
|
||||
130,
|
||||
38,
|
||||
126,
|
||||
43,
|
||||
131,
|
||||
49,
|
||||
137,
|
||||
52,
|
||||
140,
|
||||
53,
|
||||
141,
|
||||
50,
|
||||
138,
|
||||
55,
|
||||
143,
|
||||
56,
|
||||
144,
|
||||
65,
|
||||
154,
|
||||
59,
|
||||
148,
|
||||
63,
|
||||
152,
|
||||
66,
|
||||
155,
|
||||
60,
|
||||
149,
|
||||
72,
|
||||
161,
|
||||
70,
|
||||
159,
|
||||
73,
|
||||
162,
|
||||
71,
|
||||
160,
|
||||
75,
|
||||
164,
|
||||
76,
|
||||
165,
|
||||
86,
|
||||
174,
|
||||
84,
|
||||
172,
|
||||
79,
|
||||
168,
|
||||
85,
|
||||
173,
|
||||
82,
|
||||
145,
|
||||
123,
|
||||
214,
|
||||
88,
|
||||
176,
|
||||
90,
|
||||
178,
|
||||
179,
|
||||
92,
|
||||
181,
|
||||
94,
|
||||
183,
|
||||
93,
|
||||
182,
|
||||
95,
|
||||
184,
|
||||
};
|
||||
const GLint __glcMaxCode = 320;
|
||||
const GLint __glcCodeFromNameSize = 256;
|
|
@ -0,0 +1,233 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the functions needed for cleanup stack exception handling (CSEH)
|
||||
* which concept is described in details at
|
||||
* http://www.freetype.org/david/reliable-c.html
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "except.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_LIST_H
|
||||
|
||||
/* Unhandled exceptions (when area->exceptionStack.tail == NULL)
|
||||
* still need to be implemented. Such situations can occur if an exception
|
||||
* is thrown out of a try/catch block.
|
||||
*/
|
||||
|
||||
typedef struct __GLCcleanupStackNodeRec __GLCcleanupStackNode;
|
||||
typedef struct __GLCexceptContextRec __GLCexceptContext;
|
||||
|
||||
struct __GLCcleanupStackNodeRec {
|
||||
FT_ListNodeRec node;
|
||||
|
||||
void (*destructor) (void *);
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct __GLCexceptContextRec {
|
||||
FT_ListNodeRec node;
|
||||
|
||||
__glcException exception;
|
||||
FT_ListRec cleanupStack;
|
||||
jmp_buf env;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Create a context for exception handling */
|
||||
jmp_buf* __glcExceptionCreateContext(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
xContext = (__GLCexceptContext*)malloc(sizeof(__GLCexceptContext));
|
||||
if (!xContext) {
|
||||
area->failedTry = GLC_MEMORY_EXC;
|
||||
return NULL;
|
||||
}
|
||||
xContext->exception = GLC_NO_EXC;
|
||||
xContext->cleanupStack.head = NULL;
|
||||
xContext->cleanupStack.tail = NULL;
|
||||
FT_List_Add(&area->exceptionStack, (FT_ListNode)xContext);
|
||||
|
||||
return &xContext->env;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destroy the last context for exception handling */
|
||||
void __glcExceptionReleaseContext(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
xContext = (__GLCexceptContext*)area->exceptionStack.tail;
|
||||
assert(xContext);
|
||||
/* The cleanup stack must be empty */
|
||||
assert(!xContext->cleanupStack.head);
|
||||
assert(!xContext->cleanupStack.tail);
|
||||
|
||||
FT_List_Remove(&area->exceptionStack, (FT_ListNode)xContext);
|
||||
free(xContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Keep track of data to be destroyed in case of */
|
||||
void __glcExceptionPush(void (*destructor)(void*), void *data)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
__GLCcleanupStackNode *stackNode = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
xContext = (__GLCexceptContext*)area->exceptionStack.tail;
|
||||
assert(xContext);
|
||||
|
||||
stackNode = (__GLCcleanupStackNode*) malloc(sizeof(__GLCcleanupStackNode));
|
||||
if (!stackNode) {
|
||||
destructor(data);
|
||||
THROW(GLC_MEMORY_EXC);
|
||||
}
|
||||
|
||||
stackNode->destructor = destructor;
|
||||
stackNode->data = data;
|
||||
FT_List_Add(&xContext->cleanupStack, (FT_ListNode)stackNode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove the last entry of the cleanup stack, eventually destroying the
|
||||
* corresponding data if destroy != 0
|
||||
*/
|
||||
void __glcExceptionPop(int destroy)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
__GLCcleanupStackNode *stackNode = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
xContext = (__GLCexceptContext*)area->exceptionStack.tail;
|
||||
assert(xContext);
|
||||
|
||||
stackNode = (__GLCcleanupStackNode*)xContext->cleanupStack.tail;
|
||||
assert(stackNode);
|
||||
|
||||
if (destroy) {
|
||||
assert(stackNode->destructor);
|
||||
assert(stackNode->data);
|
||||
stackNode->destructor(stackNode->data);
|
||||
}
|
||||
FT_List_Remove(&xContext->cleanupStack, (FT_ListNode)stackNode);
|
||||
free(stackNode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Empty the cleanup stack and destroy the corresponding data if
|
||||
* destroy != 0
|
||||
*/
|
||||
void __glcExceptionUnwind(int destroy)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
FT_ListNode next = NULL;
|
||||
__GLCcleanupStackNode *stackNode = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
xContext = (__GLCexceptContext*)area->exceptionStack.tail;
|
||||
assert(xContext);
|
||||
|
||||
stackNode = (__GLCcleanupStackNode*)xContext->cleanupStack.head;
|
||||
|
||||
while (stackNode) {
|
||||
next = stackNode->node.next;
|
||||
if (destroy) {
|
||||
assert(stackNode->destructor);
|
||||
assert(stackNode->data);
|
||||
stackNode->destructor(stackNode->data);
|
||||
}
|
||||
free(stackNode);
|
||||
stackNode = (__GLCcleanupStackNode*)next;
|
||||
}
|
||||
|
||||
xContext->cleanupStack.head = NULL;
|
||||
xContext->cleanupStack.tail = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Throw an exception */
|
||||
jmp_buf* __glcExceptionThrow(__glcException exception)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
xContext = (__GLCexceptContext*)area->exceptionStack.tail;
|
||||
assert(xContext);
|
||||
|
||||
xContext->exception = exception;
|
||||
return &xContext->env;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Rethrow an exception that has already been catched */
|
||||
__glcException __glcExceptionCatch(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
__GLCexceptContext *xContext = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
if (area->failedTry) {
|
||||
__glcException exc = area->failedTry;
|
||||
|
||||
area->failedTry = GLC_NO_EXC;
|
||||
return exc;
|
||||
}
|
||||
|
||||
xContext = (__GLCexceptContext*)area->exceptionStack.tail;
|
||||
assert(xContext);
|
||||
|
||||
return xContext->exception;
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the functions needed for cleanup stack exception handling (CSEH)
|
||||
*/
|
||||
|
||||
#ifndef __glc_except_h
|
||||
#define __glc_except_h
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef __cpluplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef unsigned int __glcException;
|
||||
|
||||
#define GLC_NO_EXC (__glcException)0
|
||||
#define GLC_MEMORY_EXC (__glcException)1
|
||||
|
||||
jmp_buf* __glcExceptionCreateContext(void);
|
||||
void __glcExceptionReleaseContext(void);
|
||||
void __glcExceptionPush(void (*destructor)(void*), void *data);
|
||||
void __glcExceptionPop(int destroy);
|
||||
void __glcExceptionUnwind(int destroy);
|
||||
jmp_buf* __glcExceptionThrow(__glcException exception);
|
||||
__glcException __glcExceptionCatch(void);
|
||||
|
||||
#define TRY \
|
||||
do { \
|
||||
jmp_buf* __glcEnv = __glcExceptionCreateContext(); \
|
||||
if (__glcEnv && (setjmp(*__glcEnv) == 0)) {
|
||||
|
||||
#define CATCH(__e__) \
|
||||
__glcExceptionUnwind(0); \
|
||||
} \
|
||||
else { \
|
||||
__e__ = __glcExceptionCatch();
|
||||
|
||||
#define END_CATCH \
|
||||
} \
|
||||
__glcExceptionReleaseContext(); \
|
||||
} while (0);
|
||||
|
||||
#define THROW(__e__) \
|
||||
do { \
|
||||
jmp_buf* __glcEnv; \
|
||||
__glcExceptionUnwind(1); \
|
||||
__glcEnv = __glcExceptionThrow(__e__); \
|
||||
longjmp(*__glcEnv, 1); \
|
||||
} while (0);
|
||||
|
||||
#define RETHROW(__e__) \
|
||||
do { \
|
||||
jmp_buf* __glcEnv; \
|
||||
__glcExceptionReleaseContext(); \
|
||||
__glcExceptionUnwind(1); \
|
||||
__glcEnv = __glcExceptionThrow(__e__); \
|
||||
longjmp(*__glcEnv, 1); \
|
||||
} while (0);
|
||||
|
||||
#define RETURN(__value__) \
|
||||
do { \
|
||||
__glcExceptionUnwind(0); \
|
||||
__glcExceptionReleaseContext(); \
|
||||
return __value__; \
|
||||
} while(0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,707 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the so-called "Global commands" described in chapter 3.4 of the
|
||||
* GLC specs.
|
||||
*/
|
||||
|
||||
/** \defgroup global Global Commands
|
||||
* Commands to create, manage and destroy GLC contexts.
|
||||
*
|
||||
* Those commands do not use GLC context state variables and can therefore be
|
||||
* executed successfully if the issuing thread has no current GLC context.
|
||||
*
|
||||
* Each GLC context has a nonzero ID of type \b GLint. When a client is linked
|
||||
* with a GLC library, the library maintains a list of IDs that contains one
|
||||
* entry for each of the client's GLC contexts. The list is initially empty.
|
||||
*
|
||||
* Each client thread has a private GLC context ID variable that always
|
||||
* contains either the value zero, indicating that the thread has no current
|
||||
* GLC context, or the ID of the thread's current GLC context. The initial
|
||||
* value is zero.
|
||||
*
|
||||
* When the ID of a GLC context is stored in the GLC context ID variable of a
|
||||
* client thread, the context is said to be current to the thread. It is not
|
||||
* possible for a GLC context to be current simultaneously to multiple
|
||||
* threads. With the exception of the per-thread GLC error code and context ID
|
||||
* variables, all of the GLC state variables that are used during the
|
||||
* execution of a GLC command are stored in the issuing thread's current GLC
|
||||
* context. To make a context current, call glcContext().
|
||||
*
|
||||
* When a client thread issues a GLC command, the thread's current GLC context
|
||||
* executes the command.
|
||||
*
|
||||
* Note that the results of issuing a GL command when there is no current GL
|
||||
* context are undefined. Because GLC issues GL commands, you must create a GL
|
||||
* context and make it current before calling GLC.
|
||||
*
|
||||
* All other GLC commands raise \b GLC_STATE_ERROR if the issuing thread has
|
||||
* no current GLC context.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
__attribute__((constructor)) void init(void);
|
||||
__attribute__((destructor)) void fini(void);
|
||||
#else
|
||||
void _init(void);
|
||||
void _fini(void);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/* Since the common area can be accessed by any thread, this function should
|
||||
* be called before any access (read or write) to the common area. Otherwise
|
||||
* race conditions can occur. This function must also be used whenever we call
|
||||
* a function which is not reentrant (it is the case for some Fontconfig
|
||||
* entries).
|
||||
* __glcLock/__glcUnlock can be nested : they keep track of the number of
|
||||
* time they have been called and the mutex will be released as soon as
|
||||
* __glcUnlock() will be called as many time as __glcLock() was.
|
||||
*/
|
||||
void __glcLock(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
if (!area->lockState)
|
||||
#ifdef __WIN32__
|
||||
EnterCriticalSection(&__glcCommonArea.section);
|
||||
#else
|
||||
pthread_mutex_lock(&__glcCommonArea.mutex);
|
||||
#endif
|
||||
|
||||
area->lockState++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Unlock the mutex in order to allow other threads to make accesses to the
|
||||
* common area.
|
||||
* See also the note on nested calls in __glcLock's description.
|
||||
*/
|
||||
void __glcUnlock(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
assert(area->lockState);
|
||||
area->lockState--;
|
||||
|
||||
if (!area->lockState)
|
||||
#ifdef __WIN32__
|
||||
LeaveCriticalSection(&__glcCommonArea.section);
|
||||
#else
|
||||
pthread_mutex_unlock(&__glcCommonArea.mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if !defined(HAVE_TLS) && !defined(__WIN32__)
|
||||
/* This function is called each time a pthread is cancelled or exits in order
|
||||
* to free its specific area
|
||||
*/
|
||||
static void __glcFreeThreadArea(void *keyValue)
|
||||
{
|
||||
__GLCthreadArea *area = (__GLCthreadArea*)keyValue;
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
if (area) {
|
||||
/* Release the context which is current to the thread, if any */
|
||||
ctx = area->currentContext;
|
||||
if (ctx)
|
||||
ctx->isCurrent = GL_FALSE;
|
||||
free(area); /* DO NOT use __glcFree() !!! */
|
||||
}
|
||||
}
|
||||
#endif /* !HAVE_TLS && !__WIN32__ */
|
||||
|
||||
|
||||
|
||||
/* This function is called when QuesoGLC is no longer needed.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
__attribute__((destructor)) void fini(void)
|
||||
#else
|
||||
void _fini(void)
|
||||
#endif
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
#if 0
|
||||
void *key = NULL;
|
||||
#endif
|
||||
|
||||
__glcLock();
|
||||
|
||||
/* destroy remaining contexts */
|
||||
node = __glcCommonArea.contextList.head;
|
||||
while (node) {
|
||||
FT_ListNode next = node->next;
|
||||
__glcContextDestroy((__GLCcontext*)node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
#if FC_MINOR > 2 && defined(DEBUGMODE)
|
||||
FcFini();
|
||||
#endif
|
||||
|
||||
__glcUnlock();
|
||||
#ifdef __WIN32__
|
||||
DeleteCriticalSection(&__glcCommonArea.section);
|
||||
#else
|
||||
pthread_mutex_destroy(&__glcCommonArea.mutex);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Destroy the thread local storage */
|
||||
key = pthread_getspecific(__glcCommonArea.threadKey);
|
||||
if (key)
|
||||
__glcFreeThreadArea(key);
|
||||
|
||||
pthread_key_delete(__glcCommonArea.threadKey);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Routines for memory management of FreeType
|
||||
* The memory manager of our FreeType library class uses the same memory
|
||||
* allocation functions than QuesoGLC
|
||||
*/
|
||||
static void* __glcAllocFunc(FT_Memory GLC_UNUSED_ARG(inMemory), long inSize)
|
||||
{
|
||||
return malloc(inSize);
|
||||
}
|
||||
|
||||
static void __glcFreeFunc(FT_Memory GLC_UNUSED_ARG(inMemory), void *inBlock)
|
||||
{
|
||||
free(inBlock);
|
||||
}
|
||||
|
||||
static void* __glcReallocFunc(FT_Memory GLC_UNUSED_ARG(inMemory),
|
||||
long GLC_UNUSED_ARG(inCurSize),
|
||||
long inNewSize, void* inBlock)
|
||||
{
|
||||
return realloc(inBlock, inNewSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function is called before any function of QuesoGLC
|
||||
* is used. It reserves memory and initialiazes the library, hence the name.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
__attribute__((constructor)) void init(void)
|
||||
#else
|
||||
void _init(void)
|
||||
#endif
|
||||
{
|
||||
#if !defined(__WIN32__) && !defined(HAVE_TLS)
|
||||
/* A temporary variable is used to store the PTHREAD_ONCE_INIT value because
|
||||
* some platforms (namely Mac OSX) define PTHREAD_ONCE_INIT as a structure
|
||||
* initialization "{.., ..}" which is not allowed to be used by C99 anywhere
|
||||
* but at variables declaration.
|
||||
*/
|
||||
const pthread_once_t onceInit = PTHREAD_ONCE_INIT;
|
||||
#endif
|
||||
|
||||
/* Initialize fontconfig */
|
||||
if (!FcInit())
|
||||
goto FatalError;
|
||||
|
||||
__glcCommonArea.versionMajor = 0;
|
||||
__glcCommonArea.versionMinor = 2;
|
||||
|
||||
/* Create the thread-local storage for GLC errors */
|
||||
#ifdef __WIN32__
|
||||
__glcCommonArea.threadKey = TlsAlloc();
|
||||
if (__glcCommonArea.threadKey == 0xffffffff)
|
||||
goto FatalError;
|
||||
__glcCommonArea.__glcInitThreadOnce = 0;
|
||||
#elif !defined(HAVE_TLS)
|
||||
if (pthread_key_create(&__glcCommonArea.threadKey, __glcFreeThreadArea))
|
||||
goto FatalError;
|
||||
/* Now we can initialize our actual once_control variable by copying the value
|
||||
* of the temporary variable onceInit.
|
||||
*/
|
||||
__glcCommonArea.__glcInitThreadOnce = onceInit;
|
||||
#endif
|
||||
|
||||
__glcCommonArea.memoryManager.user = NULL;
|
||||
__glcCommonArea.memoryManager.alloc = __glcAllocFunc;
|
||||
__glcCommonArea.memoryManager.free = __glcFreeFunc;
|
||||
__glcCommonArea.memoryManager.realloc = __glcReallocFunc;
|
||||
|
||||
/* Initialize the list of context states */
|
||||
__glcCommonArea.contextList.head = NULL;
|
||||
__glcCommonArea.contextList.tail = NULL;
|
||||
|
||||
/* Initialize the mutex for access to the contextList array */
|
||||
#ifdef __WIN32__
|
||||
InitializeCriticalSection(&__glcCommonArea.section);
|
||||
#else
|
||||
if (pthread_mutex_init(&__glcCommonArea.mutex, NULL))
|
||||
goto FatalError;
|
||||
#endif
|
||||
|
||||
return;
|
||||
|
||||
FatalError:
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
|
||||
/* Is there a better thing to do than that ? */
|
||||
perror("GLC Fatal Error");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined __WIN32__ && !defined __GNUC__
|
||||
BOOL WINAPI DllMain(HANDLE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
|
||||
{
|
||||
switch(dwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
_init();
|
||||
return TRUE;
|
||||
case DLL_PROCESS_DETACH:
|
||||
_fini();
|
||||
return TRUE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Get the context state corresponding to a given context ID */
|
||||
static __GLCcontext* __glcGetContext(const GLint inContext)
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
__glcLock();
|
||||
for (node = __glcCommonArea.contextList.head; node; node = node->next)
|
||||
if (((__GLCcontext*)node)->id == inContext) break;
|
||||
|
||||
__glcUnlock();
|
||||
|
||||
return (__GLCcontext*)node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* This command checks whether \e inContext is the ID of one of the client's
|
||||
* GLC context and returns \b GLC_TRUE if and only if it is the case.
|
||||
* \param inContext The context ID to be tested
|
||||
* \return \b GL_TRUE if \e inContext is the ID of a GLC context,
|
||||
* \b GL_FALSE otherwise
|
||||
* \sa glcDeleteContext()
|
||||
* \sa glcGenContext()
|
||||
* \sa glcGetAllContexts()
|
||||
* \sa glcContext()
|
||||
*/
|
||||
GLboolean APIENTRY glcIsContext(GLint inContext)
|
||||
{
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
return (__glcGetContext(inContext) ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* Returns the value of the issuing thread's current GLC context ID variable
|
||||
* \return The context ID of the current thread
|
||||
* \sa glcContext()
|
||||
* \sa glcDeleteContext()
|
||||
* \sa glcGenContext()
|
||||
* \sa glcGetAllContexts()
|
||||
* \sa glcIsContext()
|
||||
*/
|
||||
GLint APIENTRY glcGetCurrentContext(void)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx)
|
||||
return 0;
|
||||
else
|
||||
return ctx->id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* Marks for deletion the GLC context identified by \e inContext. If the
|
||||
* marked context is not current to any client thread, the command deletes
|
||||
* the marked context immediatly. Otherwise, the marked context will be
|
||||
* deleted during the execution of the next glcContext() command that causes
|
||||
* it not to be current to any client thread.
|
||||
*
|
||||
* \note glcDeleteContext() does not destroy the GL objects associated with
|
||||
* the context \e inContext. Indeed for performance reasons, GLC does not keep
|
||||
* track of the GL context that contains the GL objects associated with the
|
||||
* the GLC context that is destroyed. Even if GLC would keep track of the GL
|
||||
* context, it could lead GLC to temporarily change the GL context, delete the
|
||||
* GL objects, then restore the correct GL context. In order not to adversely
|
||||
* impact the performance of most of programs, it is the responsability of the
|
||||
* user to call glcDeleteGLObjects() on a GLC context that is intended to be
|
||||
* destroyed.
|
||||
*
|
||||
* The command raises \b GLC_PARAMETER_ERROR if \e inContext is not the ID of
|
||||
* one of the client's GLC contexts.
|
||||
* \param inContext The ID of the context to be deleted
|
||||
* \sa glcGetAllContexts()
|
||||
* \sa glcIsContext()
|
||||
* \sa glcContext()
|
||||
* \sa glcGetCurrentContext()
|
||||
*/
|
||||
void APIENTRY glcDeleteContext(GLint inContext)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Lock the "Common Area" in order to prevent race conditions. Indeed, we
|
||||
* must prevent other threads to make current the context that we are
|
||||
* destroying.
|
||||
*/
|
||||
__glcLock();
|
||||
|
||||
/* verify if the context exists */
|
||||
ctx = __glcGetContext(inContext);
|
||||
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
__glcUnlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->isCurrent)
|
||||
/* The context is current to a thread : just mark for deletion */
|
||||
ctx->pendingDelete = GL_TRUE;
|
||||
else {
|
||||
/* Remove the context from the context list then destroy it */
|
||||
FT_List_Remove(&__glcCommonArea.contextList, (FT_ListNode)ctx);
|
||||
ctx->isInGlobalCommand = GL_TRUE;
|
||||
__glcContextDestroy(ctx);
|
||||
}
|
||||
|
||||
__glcUnlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* Assigns the value \e inContext to the issuing thread's current GLC context
|
||||
* ID variable. If another context is already current to the thread, no error
|
||||
* is generated but the context is released and the context identified by
|
||||
* \e inContext is made current to the thread.
|
||||
*
|
||||
* Call \e glcContext with \e inContext set to zero to release a thread's
|
||||
* current context.
|
||||
*
|
||||
* When a GLCcontext is made current to a thread, GLC issues the commands
|
||||
* \code
|
||||
* glGetString(GL_VERSION);
|
||||
* glGetString(GL_EXTENSIONS);
|
||||
* \endcode
|
||||
* and stores the returned strings. If there is no GL context current to the
|
||||
* thread, the result of the above GL commands is undefined and so is the
|
||||
* result of glcContext().
|
||||
*
|
||||
* The command raises \b GLC_PARAMETER_ERROR if \e inContext is not zero
|
||||
* and is not the ID of one of the client's GLC contexts. \n
|
||||
* The command raises \b GLC_STATE_ERROR if \e inContext is the ID of a GLC
|
||||
* context that is current to a thread other than the issuing thread. \n
|
||||
* The command raises \b GLC_STATE_ERROR if the issuing thread is executing
|
||||
* a callback function that has been called from GLC.
|
||||
* \param inContext The ID of the context to be made current
|
||||
* \sa glcGetCurrentContext()
|
||||
* \sa glcDeleteContext()
|
||||
* \sa glcGenContext()
|
||||
* \sa glcGetAllContexts()
|
||||
* \sa glcIsContext()
|
||||
*/
|
||||
void APIENTRY glcContext(GLint inContext)
|
||||
{
|
||||
__GLCcontext *currentContext = NULL;
|
||||
__GLCcontext *ctx = NULL;
|
||||
__GLCthreadArea *area = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
if (inContext < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
/* Lock the "Common Area" in order to prevent race conditions */
|
||||
__glcLock();
|
||||
|
||||
if (inContext) {
|
||||
/* verify that the context exists */
|
||||
ctx = __glcGetContext(inContext);
|
||||
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
__glcUnlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the current context of the issuing thread */
|
||||
currentContext = area->currentContext;
|
||||
|
||||
/* Check if the issuing thread is executing a callback
|
||||
* function that has been called from GLC
|
||||
*/
|
||||
if (currentContext) {
|
||||
if (currentContext->isInCallbackFunc) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
__glcUnlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Is the context already current to a thread ? */
|
||||
if (ctx->isCurrent) {
|
||||
/* If the context is current to another thread => ERROR ! */
|
||||
if (ctx != currentContext)
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
|
||||
/* If we get there, this means that the context 'inContext'
|
||||
* is already current to one thread (whether it is the issuing thread
|
||||
* or not) : there is nothing else to be done.
|
||||
*/
|
||||
__glcUnlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Release old current context if any */
|
||||
if (currentContext)
|
||||
currentContext->isCurrent = GL_FALSE;
|
||||
|
||||
/* Make the context current to the thread */
|
||||
area->currentContext = ctx;
|
||||
ctx->isCurrent = GL_TRUE;
|
||||
}
|
||||
else {
|
||||
/* inContext is null, the current thread must release its context if any */
|
||||
|
||||
/* Gets the current context state */
|
||||
currentContext = area->currentContext;
|
||||
|
||||
if (currentContext) {
|
||||
/* Deassociate the context from the issuing thread */
|
||||
area->currentContext = NULL;
|
||||
/* Release the context */
|
||||
currentContext->isCurrent = GL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute pending deletion if any. Here, the variable name 'currentContext'
|
||||
* is not appropriate any more : 'currentContext' used to be the current
|
||||
* context but it has either been replaced by another one or it has been
|
||||
* released.
|
||||
*/
|
||||
if (currentContext) {
|
||||
if (currentContext->pendingDelete) {
|
||||
assert(!currentContext->isCurrent);
|
||||
FT_List_Remove(&__glcCommonArea.contextList, (FT_ListNode)currentContext);
|
||||
currentContext->isInGlobalCommand = GL_TRUE;
|
||||
__glcContextDestroy(currentContext);
|
||||
}
|
||||
}
|
||||
|
||||
__glcUnlock();
|
||||
|
||||
/* If the issuing thread has released its context then there is no point to
|
||||
* check for OpenGL extensions.
|
||||
*/
|
||||
if (!inContext)
|
||||
return;
|
||||
|
||||
/* During its initialization, GLEW calls glGetString(GL_VERSION) and
|
||||
* glGetString(GL_EXTENSIONS) so, not only glewInit() allows to determine the
|
||||
* available extensions, but it also allows to be conformant with GLC specs
|
||||
* which require to issue those GL commands.
|
||||
*
|
||||
* Notice however that not all OpenGL implementations return a result if the
|
||||
* function glGetString() is called while no GL context is bound to the
|
||||
* current thread. Since this behaviour is not required by the GL specs, such
|
||||
* calls may just fail or lead to weird results or even crash the app (on
|
||||
* Mac OSX). It is the user's responsibility to make sure that it does not
|
||||
* happen since the GLC specs state clearly that :
|
||||
* 1. glGetString() is called by glcContext()
|
||||
* 2. the behaviour of GLC is undefined if no GL context is current while
|
||||
* issuing GL commands.
|
||||
*/
|
||||
if (glewInit() != GLEW_OK)
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* Generates a new GLC context and returns its ID.
|
||||
* \return The ID of the new context
|
||||
* \sa glcGetAllContexts()
|
||||
* \sa glcIsContext()
|
||||
* \sa glcContext()
|
||||
* \sa glcGetCurrentContext()
|
||||
*/
|
||||
GLint APIENTRY glcGenContext(void)
|
||||
{
|
||||
int newContext = 0;
|
||||
__GLCcontext *ctx = NULL;
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Create a new context */
|
||||
ctx = __glcContextCreate(0);
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
/* Lock the "Common Area" in order to prevent race conditions */
|
||||
__glcLock();
|
||||
|
||||
/* Search for the first context ID that is unused */
|
||||
if (__glcCommonArea.contextList.tail)
|
||||
newContext = ((__GLCcontext*)__glcCommonArea.contextList.tail)->id;
|
||||
|
||||
ctx->id = newContext + 1;
|
||||
|
||||
node = (FT_ListNode)ctx;
|
||||
node->data = ctx;
|
||||
FT_List_Add(&__glcCommonArea.contextList, node);
|
||||
|
||||
__glcUnlock();
|
||||
|
||||
return ctx->id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* Returns a zero terminated array of GLC context IDs that contains one entry
|
||||
* for each of the client's GLC contexts. GLC uses the ISO C library command
|
||||
* \c malloc to allocate the array. The client should use the ISO C library
|
||||
* command \c free to deallocate the array when it is no longer needed.
|
||||
* \return The pointer to the array of context IDs.
|
||||
* \sa glcContext()
|
||||
* \sa glcDeleteContext()
|
||||
* \sa glcGenContext()
|
||||
* \sa glcGetCurrentContext()
|
||||
* \sa glcIsContext()
|
||||
*/
|
||||
GLint* APIENTRY glcGetAllContexts(void)
|
||||
{
|
||||
int count = 0;
|
||||
GLint* contextArray = NULL;
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Count the number of existing contexts (whether they are current to a
|
||||
* thread or not).
|
||||
*/
|
||||
__glcLock();
|
||||
for (node = __glcCommonArea.contextList.head, count = 0; node;
|
||||
node = node->next, count++);
|
||||
|
||||
/* Allocate memory to store the array (including the zero termination value)*/
|
||||
contextArray = (GLint *)__glcMalloc(sizeof(GLint) * (count+1));
|
||||
if (!contextArray) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcUnlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Array must be null-terminated */
|
||||
contextArray[count] = 0;
|
||||
|
||||
/* Copy the context IDs to the array */
|
||||
for (node = __glcCommonArea.contextList.tail; node; node = node->prev)
|
||||
contextArray[--count] = ((__GLCcontext*)node)->id;
|
||||
|
||||
__glcUnlock();
|
||||
|
||||
return contextArray;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup global
|
||||
* Retrieves the value of the issuing thread's GLC error code variable,
|
||||
* assigns the value \b GLC_NONE to that variable, and returns the retrieved
|
||||
* value.
|
||||
* \note In contrast to the GL function \c glGetError, \e glcGetError only
|
||||
* returns one error, not a list of errors.
|
||||
* \return An error code from the table below : \n\n
|
||||
* <center>
|
||||
* <table>
|
||||
* <caption>Error codes</caption>
|
||||
* <tr>
|
||||
* <td>Name</td> <td>Enumerant</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_NONE</b></td> <td>0x0000</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_PARAMETER_ERROR</b></td> <td>0x0040</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_RESOURCE_ERROR</b></td> <td>0x0041</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_STATE_ERROR</b></td> <td>0x0042</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </center>
|
||||
*/
|
||||
GLCenum APIENTRY glcGetError(void)
|
||||
{
|
||||
GLCenum error = GLC_NONE;
|
||||
__GLCthreadArea * area = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
error = area->errorState;
|
||||
__glcRaiseError(GLC_NONE);
|
||||
return error;
|
||||
}
|
|
@ -0,0 +1,386 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of all private functions that are used throughout the library.
|
||||
*/
|
||||
|
||||
#ifndef __glc_internal_h
|
||||
#define __glc_internal_h
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(DEBUGMODE) && !defined(NDEBUG)
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_LIBGLEW
|
||||
#include <GL/glew.h>
|
||||
#else
|
||||
#include "GL/glew.h"
|
||||
#endif
|
||||
#define GLCAPI GLEWAPI
|
||||
#include "GL/glc.h"
|
||||
#include "qglc_config.h"
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#if defined(HAVE_FT_CACHE) && defined(FT_CACHE_H)
|
||||
#define GLC_FT_CACHE
|
||||
#endif
|
||||
|
||||
#define GLCchar8 FcChar8
|
||||
#define GLCchar16 FcChar16
|
||||
#define GLCchar32 FcChar32
|
||||
#define GLCuint FT_UInt
|
||||
#define GLClong FT_Long
|
||||
#define GLCulong FT_ULong
|
||||
|
||||
#include "ofont.h"
|
||||
|
||||
#define GLC_OUT_OF_RANGE_LEN 11
|
||||
#define GLC_EPSILON 1E-6
|
||||
#define GLC_POINT_SIZE 128
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define GLC_UNUSED_ARG(_arg) GLC_UNUSED_ ## _arg __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
# define GLC_UNUSED_ARG(_arg) /*@unused@*/ GLC_UNUSED_ ## _arg
|
||||
#else
|
||||
# define GLC_UNUSED_ARG(_arg) GLC_UNUSED_ ## _arg
|
||||
#endif
|
||||
|
||||
/* Definition of the GLC_INIT_THREAD macro : it is some sort of an equivalent to
|
||||
* XInitThreads(). It allows to get rid of pthread_get_specific()/TlsGetValue()
|
||||
* when only one thread is used and to fallback to the usual thread management
|
||||
* if more than one thread is used.
|
||||
* If Thread Local Storage is used the macro does nothing.
|
||||
*/
|
||||
#ifdef __WIN32__
|
||||
# define GLC_INIT_THREAD() \
|
||||
if (!InterlockedCompareExchange(&__glcCommonArea.__glcInitThreadOnce, 1, 0)) \
|
||||
__glcInitThread();
|
||||
#elif !defined(HAVE_TLS)
|
||||
# define GLC_INIT_THREAD() \
|
||||
pthread_once(&__glcCommonArea.__glcInitThreadOnce, __glcInitThread);
|
||||
#else
|
||||
#define GLC_INIT_THREAD()
|
||||
#endif
|
||||
|
||||
/* Definition of the GLC_GET_THREAD_AREA macro */
|
||||
#ifdef __WIN32__
|
||||
# define GLC_GET_THREAD_AREA() \
|
||||
(((__glcCommonArea.threadID == GetCurrentThreadId()) && __glcThreadArea) ? \
|
||||
__glcThreadArea : __glcGetThreadArea())
|
||||
#elif !defined(HAVE_TLS)
|
||||
# define GLC_GET_THREAD_AREA() \
|
||||
((pthread_equal(__glcCommonArea.threadID, pthread_self()) && __glcThreadArea) ? \
|
||||
__glcThreadArea : __glcGetThreadArea())
|
||||
#else
|
||||
# define GLC_GET_THREAD_AREA() &__glcTlsThreadArea
|
||||
#endif
|
||||
|
||||
/* Definition of the GLC_GET_CURRENT_CONTEXT macro */
|
||||
#ifdef __WIN32__
|
||||
# define GLC_GET_CURRENT_CONTEXT() \
|
||||
(((__glcCommonArea.threadID == GetCurrentThreadId()) && __glcThreadArea) ? \
|
||||
__glcThreadArea->currentContext : __glcGetCurrent())
|
||||
#elif !defined(HAVE_TLS)
|
||||
# define GLC_GET_CURRENT_CONTEXT() \
|
||||
((pthread_equal(__glcCommonArea.threadID, pthread_self()) && __glcThreadArea) ? \
|
||||
__glcThreadArea->currentContext : __glcGetCurrent())
|
||||
#else
|
||||
#define GLC_GET_CURRENT_CONTEXT() __glcTlsThreadArea.currentContext
|
||||
#endif
|
||||
|
||||
/* ceil() and floor() macros for 26.6 fixed integers */
|
||||
#define GLC_CEIL_26_6(x) (((x) < 0) ? ((x) & -64) : ((x) + 63) & -64)
|
||||
#define GLC_FLOOR_26_6(x) (((x) < 0) ? (((x) - 63) & -64) : ((x) & -64))
|
||||
|
||||
typedef struct __GLCdataCodeFromNameRec __GLCdataCodeFromName;
|
||||
typedef struct __GLCcharacterRec __GLCcharacter;
|
||||
|
||||
struct __GLCrendererDataRec {
|
||||
GLfloat vector[8]; /* Current coordinates */
|
||||
GLfloat tolerance; /* Chordal tolerance */
|
||||
__GLCarray* vertexArray; /* Array of vertices */
|
||||
__GLCarray* controlPoints; /* Array of control points */
|
||||
__GLCarray* endContour; /* Array of contour limits */
|
||||
__GLCarray* vertexIndices; /* Array of vertex indices */
|
||||
__GLCarray* geomBatches; /* Array of geometric batches */
|
||||
GLfloat* transformMatrix; /* Transformation matrix from the
|
||||
object space to the viewport */
|
||||
GLfloat halfWidth;
|
||||
GLfloat halfHeight;
|
||||
};
|
||||
|
||||
struct __GLCdataCodeFromNameRec {
|
||||
GLint code;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
struct __GLCgeomBatchRec {
|
||||
GLenum mode;
|
||||
GLint length;
|
||||
GLuint start;
|
||||
GLuint end;
|
||||
};
|
||||
|
||||
struct __GLCcharacterRec {
|
||||
GLint code;
|
||||
__GLCfont* font;
|
||||
__GLCglyph* glyph;
|
||||
GLfloat advance[2];
|
||||
};
|
||||
|
||||
/* Those functions are used to protect against race conditions whenever we try
|
||||
* to access the common area or functions which are not multi-threaded.
|
||||
*/
|
||||
void __glcLock(void);
|
||||
void __glcUnlock(void);
|
||||
|
||||
/* Callback function type that is called by __glcProcessChar().
|
||||
* It allows to unify the character processing before the rendering or the
|
||||
* measurement of a character : __glcProcessChar() is called first (see below)
|
||||
* then the callback function of type __glcProcessCharFunc is called by
|
||||
* __glcProcessChar(). Two functions are defined according to this type :
|
||||
* __glcRenderChar() for rendering and __glcGetCharMetric() for measurement.
|
||||
*/
|
||||
typedef void* (*__glcProcessCharFunc)(const GLint inCode,
|
||||
const GLint inPrevCode,
|
||||
const GLboolean inIsRTL,
|
||||
const __GLCfont* inFont,
|
||||
__GLCcontext* inContext,
|
||||
const void* inProcessCharData,
|
||||
const GLboolean inMultipleChars);
|
||||
|
||||
/* Process the character in order to find a font that maps the code and to
|
||||
* render the corresponding glyph. Replacement code or the '\<hexcode>'
|
||||
* character sequence is issued if necessary.
|
||||
* 'inCode' must be given in UCS-4 format
|
||||
*/
|
||||
extern void* __glcProcessChar(__GLCcontext *inContext, const GLint inCode,
|
||||
__GLCcharacter* inPrevCode,
|
||||
const GLboolean inIsRTL,
|
||||
const __glcProcessCharFunc inProcessCharFunc,
|
||||
const void* inProcessCharData);
|
||||
|
||||
/* Render scalable characters using either the GLC_LINE style or the
|
||||
* GLC_TRIANGLE style
|
||||
*/
|
||||
extern void __glcRenderCharScalable(const __GLCfont* inFont,
|
||||
const __GLCcontext* inContext,
|
||||
GLfloat* inTransformMatrix,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY,
|
||||
__GLCglyph* inGlyph);
|
||||
|
||||
/* QuesoGLC own allocation and memory management routines */
|
||||
#ifdef DEBUGMODE
|
||||
extern void* __glcMalloc(size_t size);
|
||||
extern void __glcFree(void* ptr);
|
||||
extern void* __glcRealloc(void* ptr, size_t size);
|
||||
#else
|
||||
static inline void* __glcMalloc(size_t size)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
static inline void __glcFree(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
static inline void* __glcRealloc(void *ptr, size_t size)
|
||||
{
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Arrays that contain the Unicode name of characters */
|
||||
extern const __GLCdataCodeFromName __glcCodeFromNameArray[];
|
||||
extern const GLint __glcNameFromCodeArray[];
|
||||
extern const GLint __glcMaxCode;
|
||||
extern const GLint __glcCodeFromNameSize;
|
||||
|
||||
/* Find a Unicode name from its code */
|
||||
extern const GLCchar8* __glcNameFromCode(const GLint code);
|
||||
|
||||
/* Find a Unicode code from its name */
|
||||
extern GLint __glcCodeFromName(const GLCchar8* name);
|
||||
|
||||
/* Duplicate a string and convert if from any Unicode format to UTF-8 format */
|
||||
extern GLCchar8* __glcConvertToUtf8(const GLCchar* inString,
|
||||
const GLint inStringType);
|
||||
|
||||
/* Duplicate a string to the context buffer and convert it from UTF-8 format to
|
||||
* any Unicode format.
|
||||
*/
|
||||
extern GLCchar* __glcConvertFromUtf8ToBuffer(__GLCcontext* This,
|
||||
const GLCchar8* inString);
|
||||
|
||||
/* Duplicate a counted string to the context buffer and convert it from any
|
||||
* Unicode format to UTF-8 format.
|
||||
*/
|
||||
extern GLCchar8* __glcConvertCountedStringToUtf8(const GLint inCount,
|
||||
const GLCchar* inString,
|
||||
const GLint inStringType);
|
||||
|
||||
/* Convert a UCS-4 character code into the current string type. The result is
|
||||
* stored in a GLint. This function is needed since the GLC specs store
|
||||
* individual character codes in GLint whatever is their string type.
|
||||
*/
|
||||
extern GLint __glcConvertUcs4ToGLint(__GLCcontext *inContext, GLint inCode);
|
||||
|
||||
/* Convert a character encoded in the current string type to the UCS-4 format.
|
||||
* This function is needed since the GLC specs store individual character
|
||||
* codes in GLint whatever is their string type.
|
||||
*/
|
||||
extern GLint __glcConvertGLintToUcs4(const __GLCcontext *inContext,
|
||||
GLint inCode);
|
||||
|
||||
/* Verify that the thread has a current context and that the master identified
|
||||
* by 'inMaster' exists. Returns the master object corresponding to the master
|
||||
* ID 'inMaster'.
|
||||
*/
|
||||
extern __GLCmaster* __glcVerifyMasterParameters(const GLint inMaster);
|
||||
|
||||
/* Verify that the thread has a current context and that the font identified
|
||||
* by 'inFont' exists.
|
||||
*/
|
||||
extern __GLCfont* __glcVerifyFontParameters(GLint inFont);
|
||||
|
||||
/* Do the actual job of glcAppendFont(). This function can be called as an
|
||||
* internal version of glcAppendFont() where the current GLC context is already
|
||||
* determined and the font ID has been resolved in its corresponding __GLCfont
|
||||
* object.
|
||||
*/
|
||||
extern void __glcAppendFont(__GLCcontext* inContext, __GLCfont* inFont);
|
||||
|
||||
/* This internal function deletes the font identified by inFont (if any) and
|
||||
* creates a new font based on the pattern 'inPattern'. The resulting font is
|
||||
* added to the list GLC_FONT_LIST.
|
||||
*/
|
||||
extern __GLCfont* __glcNewFontFromMaster(GLint inFontID, __GLCmaster* inMaster,
|
||||
__GLCcontext *inContext, GLint inCode);
|
||||
|
||||
/* This internal function tries to open the face file which name is identified
|
||||
* by 'inFace'. If it succeeds, it closes the previous face and stores the new
|
||||
* face attributes in the __GLCfont object "inFont". Otherwise, it leaves the
|
||||
* font unchanged. GL_TRUE or GL_FALSE are returned to indicate if the function
|
||||
* succeeded or not.
|
||||
*/
|
||||
extern GLboolean __glcFontFace(__GLCfont* inFont, const GLCchar8* inFace,
|
||||
__GLCcontext *inContext);
|
||||
|
||||
/* Allocate a new ID for a font and store it in a special list so that the same
|
||||
* ID is not allocated twice.
|
||||
*/
|
||||
GLint __glcGenFontID(__GLCcontext* inContext);
|
||||
|
||||
#ifndef HAVE_TLS
|
||||
/* Return a struct which contains thread specific info. If the platform supports
|
||||
* pointers for thread-local storage (TLS) then __glcGetThreadArea is replaced
|
||||
* by a macro that returns a thread-local pointer. Otherwise, a function is
|
||||
* called to return the structure using pthread_get_specific (POSIX) or
|
||||
* TlsGetValue (WIN32) which are much slower.
|
||||
*/
|
||||
extern __GLCthreadArea* __glcGetThreadArea(void);
|
||||
#endif
|
||||
|
||||
/* Raise an error.
|
||||
* See also remarks above about TLS pointers.
|
||||
*/
|
||||
#ifdef HAVE_TLS
|
||||
#define __glcRaiseError(inError) \
|
||||
if (!__glcTlsThreadArea.errorState || ! (inError)) \
|
||||
__glcTlsThreadArea.errorState = (inError)
|
||||
#else
|
||||
extern void __glcRaiseError(GLCenum inError);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_TLS
|
||||
/* Return the current context state.
|
||||
* See also remarks above about TLS pointers.
|
||||
*/
|
||||
extern __GLCcontext* __glcGetCurrent(void);
|
||||
#endif
|
||||
|
||||
/* Compute an optimal size for the glyph to be rendered on the screen (if no
|
||||
* display list is currently building).
|
||||
*/
|
||||
extern void __glcGetScale(const __GLCcontext* inContext,
|
||||
GLfloat* outTransformMatrix,
|
||||
GLfloat* outScaleX, GLfloat* outScaleY);
|
||||
|
||||
/* Convert 'inString' (stored in logical order) to UCS4 format and return a
|
||||
* copy of the converted string in visual order.
|
||||
*/
|
||||
extern GLCchar32* __glcConvertToVisualUcs4(__GLCcontext* inContext,
|
||||
GLboolean *outIsRTL,
|
||||
GLint *outLength,
|
||||
const GLCchar* inString);
|
||||
|
||||
/* Convert 'inCount' characters of 'inString' (stored in logical order) to UCS4
|
||||
* format and return a copy of the converted string in visual order.
|
||||
*/
|
||||
extern GLCchar32* __glcConvertCountedStringToVisualUcs4(__GLCcontext* inContext,
|
||||
GLboolean *outIsRTL,
|
||||
const GLCchar* inString,
|
||||
const GLint inCount);
|
||||
|
||||
#ifdef GLC_FT_CACHE
|
||||
/* Callback function used by the FreeType cache manager to open a given face */
|
||||
extern FT_Error __glcFileOpen(FTC_FaceID inFile, FT_Library inLibrary,
|
||||
FT_Pointer inData, FT_Face* outFace);
|
||||
|
||||
/* Rename FTC_Manager_LookupFace for old freetype versions */
|
||||
# if FREETYPE_MAJOR == 2 \
|
||||
&& (FREETYPE_MINOR < 1 \
|
||||
|| (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 8))
|
||||
# define FTC_Manager_LookupFace FTC_Manager_Lookup_Face
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Save the GL State in a structure */
|
||||
extern void __glcSaveGLState(__GLCglState* inGLState,
|
||||
const __GLCcontext* inContext,
|
||||
const GLboolean inAll);
|
||||
|
||||
/* Restore the GL State from a structure */
|
||||
extern void __glcRestoreGLState(const __GLCglState* inGLState,
|
||||
const __GLCcontext* inContext,
|
||||
const GLboolean inAll);
|
||||
|
||||
#ifdef GLEW_MX
|
||||
/* Macro/function for GLEW so that it can get a context */
|
||||
GLEWAPI GLEWContext* __glcGetGlewContext(void);
|
||||
#define glewGetContext() __glcGetGlewContext()
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_TLS
|
||||
/* This function initializes the thread management of QuesoGLC when TLS is not
|
||||
* available. It must be called once (see the macro GLC_INIT_THREAD)
|
||||
*/
|
||||
extern void __glcInitThread(void);
|
||||
#endif
|
||||
|
||||
extern int __glcdeCasteljauConic(void *inUserData);
|
||||
extern int __glcdeCasteljauCubic(void *inUserData);
|
||||
|
||||
#endif /* __glc_internal_h */
|
|
@ -0,0 +1,590 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the so-called "Master commands" described in chapter 3.6 of the GLC
|
||||
* specs.
|
||||
*/
|
||||
|
||||
/** \defgroup master Master Commands
|
||||
* Commands to create, manage and destroy masters.
|
||||
*
|
||||
* A master is a representation of a font that is stored outside QuesoGLC in a
|
||||
* standard format such as TrueType or Type1.
|
||||
*
|
||||
* Every master has an associated character map. A character map is a table of
|
||||
* entries that maps integer values to the name string that identifies the
|
||||
* characters. Unlike fonts character maps, the character map of a master can
|
||||
* not be modified.
|
||||
*
|
||||
* QuesoGLC maps the font files into master objects that are visible through
|
||||
* the GLC API. A group of font files from a single typeface family will be
|
||||
* mapped into a single GLC master object that has multiple faces. For
|
||||
* example, the files \c Courier.pfa, \c Courier-Bold.pfa,
|
||||
* \c Courier-BoldOblique.pfa, and \c Courier-Oblique.pfa are visible through
|
||||
* the GLC API as a single master with \c GLC_VENDOR="Adobe",
|
||||
* \c GLC_FAMILY="Courier", \c GLC_MASTER_FORMAT="Type1", \c GLC_FACE_COUNT=4
|
||||
* and \c GLC_FACE_LIST=("Regular", "Bold", "Bold Oblique", "Oblique")
|
||||
*
|
||||
* Some GLC commands have a parameter \e inMaster. This parameter is an offset
|
||||
* from the the first element in the GLC master list. The command raises
|
||||
* \b GLC_PARAMETER_ERROR if \e inMaster is less than zero or is greater than
|
||||
* or equal to the value of the variable \b GLC_MASTER_COUNT.
|
||||
*/
|
||||
|
||||
#if defined(__WIN32__) || defined(_MSC_VER)
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
|
||||
/* Most master commands need to check that :
|
||||
* 1. The current thread owns a context state
|
||||
* 2. The master identifier 'inMaster' is legal
|
||||
* This internal function does both checks and returns the pointer to the
|
||||
* __glcMaster object that is identified by 'inMaster'.
|
||||
*/
|
||||
__GLCmaster* __glcVerifyMasterParameters(const GLint inMaster)
|
||||
{
|
||||
const __GLCcontext *ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verify if the master identifier is in legal bounds */
|
||||
if (inMaster >= GLC_ARRAY_LENGTH(ctx->masterHashTable)) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return __glcMasterCreate(inMaster, ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command returns a string from a string list that is an attribute of
|
||||
* the master identified by \e inMaster. The string list is identified by
|
||||
* \e inAttrib. The command returns the string at offset \e inIndex from the
|
||||
* first element in this string list. Below are the string list attributes
|
||||
* associated with each GLC master and font and their element count attributes:
|
||||
* <center>
|
||||
* <table>
|
||||
* <caption>Master/font string list attributes</caption>
|
||||
* <tr>
|
||||
* <td>Name</td> <td>Enumerant</td> <td>Element count attribute</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_CHAR_LIST</b></td>
|
||||
* <td>0x0050</td>
|
||||
* <td><b>GLC_CHAR_COUNT</b></td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_FACE_LIST</b></td>
|
||||
* <td>0x0051</td>
|
||||
* <td><b>GLC_FACE_COUNT</b></td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </center>
|
||||
* \n The command raises \b GLC_PARAMETER_ERROR if \e inIndex is less than
|
||||
* zero or is greater than or equal to the value of the list element count
|
||||
* attribute.
|
||||
* \param inMaster Master from which an attribute is required.
|
||||
* \param inAttrib String list that contains the desired attribute.
|
||||
* \param inIndex Offset from the first element of the list associated with
|
||||
* \e inAttrib.
|
||||
* \return The string at offset \e inIndex from the first element of the
|
||||
* string list identified by \e inAttrib.
|
||||
* \sa glcGetMasterMap()
|
||||
* \sa glcGetMasterc()
|
||||
* \sa glcGetMasteri()
|
||||
*/
|
||||
const GLCchar* APIENTRY glcGetMasterListc(GLint inMaster, GLCenum inAttrib,
|
||||
GLint inIndex)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
__GLCmaster *master = NULL;
|
||||
__GLCcharMap *charMap = NULL;
|
||||
const GLCchar8* string = NULL;
|
||||
GLCchar8* faceName = NULL;
|
||||
GLCchar* element = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check some parameters.
|
||||
* NOTE : the verification of some parameters needs to get the current
|
||||
* context state but since we are supposed to check parameters
|
||||
* _before_ the context state, we are done !
|
||||
*/
|
||||
switch(inAttrib) {
|
||||
case GLC_CHAR_LIST:
|
||||
case GLC_FACE_LIST:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
|
||||
/* Verify if inIndex is in legal bounds */
|
||||
if (inIndex < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
|
||||
/* Verify that the thread has a current context and that the master
|
||||
* identified by 'inMaster' exists.
|
||||
*/
|
||||
master = __glcVerifyMasterParameters(inMaster);
|
||||
if (!master)
|
||||
return (GLCchar*)GLC_NONE;
|
||||
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
|
||||
/* return the requested attribute */
|
||||
switch(inAttrib) {
|
||||
case GLC_CHAR_LIST:
|
||||
charMap = __glcCharMapCreate(master, ctx);
|
||||
if (!charMap) {
|
||||
__glcMasterDestroy(master);
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
string = __glcCharMapGetCharNameByIndex(charMap, inIndex);
|
||||
if (!string) {
|
||||
__glcMasterDestroy(master);
|
||||
__glcCharMapDestroy(charMap);
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
break;
|
||||
case GLC_FACE_LIST:
|
||||
/* Get the face name */
|
||||
faceName = __glcMasterGetFaceName(master, ctx, inIndex);
|
||||
string = (const GLCchar8*)faceName;
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
|
||||
|
||||
/* Convert it from UTF-8 to the current string type and return */
|
||||
element = __glcConvertFromUtf8ToBuffer(ctx, string);
|
||||
__glcMasterDestroy(master);
|
||||
if (charMap)
|
||||
__glcCharMapDestroy(charMap);
|
||||
else
|
||||
free(faceName);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command returns the string name of the character that the master
|
||||
* identified by \e inMaster maps \e inCode to.
|
||||
*
|
||||
* Every master has associated with it a master map, which is a table of
|
||||
* entries that map integer values to the name string that identifies the
|
||||
* character.
|
||||
*
|
||||
* Every character code used in QuesoGLC is an element of the Unicode
|
||||
* Character Database (UCD) defined by the standards ISO/IEC 10646:2003 and
|
||||
* Unicode 4.0.1 (unless otherwise specified). A Unicode code point is denoted
|
||||
* as \e U+hexcode, where \e hexcode is a sequence of hexadecimal digits. Each
|
||||
* Unicode code point corresponds to a character that has a unique name
|
||||
* string. For example, the code \e U+41 corresponds to the character
|
||||
* <em>LATIN CAPITAL LETTER A</em>.
|
||||
*
|
||||
* If the master does not map \e inCode, the command returns \b GLC_NONE.
|
||||
* \note While you cannot change the map of a master, you can change the map
|
||||
* of a font using glcFontMap().
|
||||
* \param inMaster The integer ID of the master from which to select the
|
||||
* character.
|
||||
* \param inCode The integer ID of character in the master map.
|
||||
* \return The string name of the character that \e inCode is mapped to.
|
||||
* \sa glcGetMasterListc()
|
||||
* \sa glcGetMasterc()
|
||||
* \sa glcGetMasteri()
|
||||
*/
|
||||
const GLCchar* APIENTRY glcGetMasterMap(GLint inMaster, GLint inCode)
|
||||
{
|
||||
__GLCmaster *master = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
master = __glcVerifyMasterParameters(inMaster);
|
||||
if (master) {
|
||||
__GLCcontext *ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
__GLCcharMap* charMap = NULL;
|
||||
GLCchar* result = NULL;
|
||||
GLint code = 0;
|
||||
const GLCchar8* name = NULL;
|
||||
|
||||
charMap = __glcCharMapCreate(master, ctx);
|
||||
__glcMasterDestroy(master);
|
||||
if (!charMap)
|
||||
return (GLCchar*)GLC_NONE;
|
||||
|
||||
/* Get the character code converted to the UCS-4 format */
|
||||
code = __glcConvertGLintToUcs4(ctx, inCode);
|
||||
if (code < 0) {
|
||||
__glcCharMapDestroy(charMap);
|
||||
return (GLCchar*)GL_NONE;
|
||||
}
|
||||
|
||||
name = __glcCharMapGetCharName(charMap, code);
|
||||
__glcCharMapDestroy(charMap);
|
||||
if (!name)
|
||||
return (GLCchar*)GLC_NONE;
|
||||
|
||||
result = __glcConvertFromUtf8ToBuffer(ctx, name);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command returns a string attribute of the master identified by
|
||||
* \e inMaster. The table below lists the string attributes that are
|
||||
* associated with each GLC master and font.
|
||||
* <center>
|
||||
* <table>
|
||||
* <caption>Master/font string attributes</caption>
|
||||
* <tr>
|
||||
* <td>Name</td> <td>Enumerant</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_FAMILY</b></td> <td>0x0060</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_MASTER_FORMAT</b></td> <td>0x0061</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_VENDOR</b></td> <td>0x0062</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_VERSION</b></td> <td>0x0063</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_FULL_NAME_SGI</b></td> <td>0x8002</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </center>
|
||||
* \param inMaster The master for which an attribute value is required.
|
||||
* \param inAttrib The attribute for which the value is required.
|
||||
* \return The value that is associated with the attribute \e inAttrib.
|
||||
* \sa glcGetMasteri()
|
||||
* \sa glcGetMasterMap()
|
||||
* \sa glcGetMasterListc()
|
||||
*/
|
||||
const GLCchar* APIENTRY glcGetMasterc(GLint inMaster, GLCenum inAttrib)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
const GLCchar *buffer = NULL;
|
||||
__GLCmaster* master = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check parameter inAttrib */
|
||||
switch(inAttrib) {
|
||||
case GLC_FAMILY:
|
||||
case GLC_MASTER_FORMAT:
|
||||
case GLC_VENDOR:
|
||||
case GLC_VERSION:
|
||||
case GLC_FULL_NAME_SGI:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return (GLCchar*)GLC_NONE;
|
||||
}
|
||||
|
||||
/* Verify that the thread has a current context and that the master
|
||||
* identified by 'inMaster' exists.
|
||||
*/
|
||||
master = __glcVerifyMasterParameters(inMaster);
|
||||
if (!master)
|
||||
return (GLCchar*)GLC_NONE;
|
||||
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
buffer = __glcMasterGetInfo(master, ctx, inAttrib);
|
||||
|
||||
__glcMasterDestroy(master);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command returns an integer attribute of the master identified by
|
||||
* \e inMaster. The attribute is identified by \e inAttrib. The table below
|
||||
* lists the integer attributes that are associated with each GLC master
|
||||
* and font.
|
||||
* <center>
|
||||
* <table>
|
||||
* <caption>Master/font integer attributes</caption>
|
||||
* <tr>
|
||||
* <td>Name</td> <td>Enumerant</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_CHAR_COUNT</b></td> <td>0x0070</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_FACE_COUNT</b></td> <td>0x0071</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_IS_FIXED_PITCH</b></td> <td>0x0072</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_MAX_MAPPED_CODE</b></td> <td>0x0073</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_MIN_MAPPED_CODE</b></td> <td>0x0074</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </center>
|
||||
* \n If the requested master attribute is \b GLC_IS_FIXED_PITCH then the
|
||||
* command returns \b GL_TRUE if and only if each face of the master
|
||||
* identified by \e inMaster has a fixed pitch.
|
||||
* \param inMaster The master for which an attribute value is required.
|
||||
* \param inAttrib The attribute for which the value is required.
|
||||
* \return The value of the attribute \e inAttrib of the master identified
|
||||
* by \e inMaster.
|
||||
* \sa glcGetMasterc()
|
||||
* \sa glcGetMasterMap()
|
||||
* \sa glcGetMasterListc()
|
||||
*/
|
||||
GLint APIENTRY glcGetMasteri(GLint inMaster, GLCenum inAttrib)
|
||||
{
|
||||
GLint count = 0;
|
||||
__GLCmaster *master = NULL;
|
||||
__GLCcharMap* charMap = NULL;
|
||||
__GLCcontext* ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check parameter inAttrib */
|
||||
switch(inAttrib) {
|
||||
case GLC_CHAR_COUNT:
|
||||
case GLC_FACE_COUNT:
|
||||
case GLC_IS_FIXED_PITCH:
|
||||
case GLC_MAX_MAPPED_CODE:
|
||||
case GLC_MIN_MAPPED_CODE:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
/* Verify that the thread has a current context and that the master
|
||||
* identified by 'inMaster' exists.
|
||||
*/
|
||||
master = __glcVerifyMasterParameters(inMaster);
|
||||
if (!master)
|
||||
return GLC_NONE;
|
||||
|
||||
if (inAttrib == GLC_IS_FIXED_PITCH) {
|
||||
/* Is this a fixed font ? */
|
||||
GLboolean fixed = __glcMasterIsFixedPitch(master);
|
||||
|
||||
__glcMasterDestroy(master);
|
||||
return fixed;
|
||||
}
|
||||
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
|
||||
if (inAttrib != GLC_FACE_COUNT) {
|
||||
charMap = __glcCharMapCreate(master, ctx);
|
||||
if (!charMap) {
|
||||
__glcMasterDestroy(master);
|
||||
return GLC_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the requested attribute */
|
||||
switch(inAttrib) {
|
||||
case GLC_CHAR_COUNT:
|
||||
count = __glcCharMapGetCount(charMap);
|
||||
break;
|
||||
case GLC_FACE_COUNT:
|
||||
count = __glcMasterFaceCount(master, ctx);
|
||||
break;
|
||||
case GLC_MAX_MAPPED_CODE:
|
||||
count = __glcCharMapGetMaxMappedCode(charMap);
|
||||
break;
|
||||
case GLC_MIN_MAPPED_CODE:
|
||||
count = __glcCharMapGetMinMappedCode(charMap);
|
||||
break;
|
||||
}
|
||||
|
||||
if (charMap)
|
||||
__glcCharMapDestroy(charMap);
|
||||
__glcMasterDestroy(master);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Common subroutine to add a catalog to the current context. It is called
|
||||
* either by glcAppendCatalog() or by glcPrependCatalog().
|
||||
*/
|
||||
static void __glcAddCatalog(const GLCchar* inCatalog, const GLboolean inAppend)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
struct stat dirStat;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* If inCatalog is NULL then there is no point in continuing */
|
||||
if (!inCatalog)
|
||||
return;
|
||||
|
||||
/* Check that 'inCatalog' points to a directory that can be read */
|
||||
#ifdef __WIN32__
|
||||
if (_access((const char*)inCatalog, 0)) {
|
||||
#else
|
||||
if (access((const char *)inCatalog, R_OK) < 0) {
|
||||
#endif
|
||||
/* May be something more explicit should be done ? */
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
/* Check that 'inCatalog' is a directory */
|
||||
if (stat((const char *)inCatalog, &dirStat) < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
#ifdef __WIN32__
|
||||
if (!(dirStat.st_mode & _S_IFDIR)) {
|
||||
#else
|
||||
if (!S_ISDIR(dirStat.st_mode)) {
|
||||
#endif
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify that the thread owns a context */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inAppend)
|
||||
__glcContextAppendCatalog(ctx, inCatalog);
|
||||
else
|
||||
__glcContextPrependCatalog(ctx, inCatalog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command appends the string \e inCatalog to the list
|
||||
* \b GLC_CATALOG_LIST.
|
||||
*
|
||||
* The catalog is represented as a zero-terminated string. The interpretation
|
||||
* of this string is specified by the value that has been set using
|
||||
* glcStringType().
|
||||
*
|
||||
* A catalog is a path to a list of masters. A master is a representation of a
|
||||
* font that is stored outside QuesoGLC in a standard format such as TrueType
|
||||
* or Type1.
|
||||
*
|
||||
* A catalog defines the list of masters that can be instantiated (that is, be
|
||||
* used as fonts) in a GLC context.
|
||||
*
|
||||
* A font is a styllistically consistent set of glyphs that can be used to
|
||||
* render some set of characters. Each font has a family name (for example
|
||||
* Palatino) and a state variable that selects one of the faces (for example
|
||||
* regular, bold, italic, bold italic) that the font contains. A typeface is
|
||||
* the combination of a family and a face (for example Palatino Bold).
|
||||
* \param inCatalog The catalog to append to the list \b GLC_CATALOG_LIST
|
||||
* \sa glcGetList() with argument \b GLC_CATALOG_LIST
|
||||
* \sa glcGeti() with argument \b GLC_CATALOG_COUNT
|
||||
* \sa glcPrependCatalog()
|
||||
* \sa glcRemoveCatalog()
|
||||
*/
|
||||
void APIENTRY glcAppendCatalog(const GLCchar* inCatalog)
|
||||
{
|
||||
__glcAddCatalog(inCatalog, GL_TRUE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command prepends the string \e inCatalog to the list
|
||||
* \b GLC_CATALOG_LIST
|
||||
* \param inCatalog The catalog to prepend to the list \b GLC_CATALOG_LIST
|
||||
* \sa glcAppendCatalog()
|
||||
* \sa glcRemoveCatalog()
|
||||
*/
|
||||
void APIENTRY glcPrependCatalog(const GLCchar* inCatalog)
|
||||
{
|
||||
__glcAddCatalog(inCatalog, GL_FALSE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup master
|
||||
* This command removes a string from the list \b GLC_CATALOG_LIST. It removes
|
||||
* the string at offset \e inIndex from the first element in the list. The
|
||||
* command raises \b GLC_PARAMETER_ERROR if \e inIndex is less than zero or is
|
||||
* greater than or equal to the value of the variable \b GLC_CATALOG_COUNT.
|
||||
*
|
||||
* QuesoGLC also removes the masters that are defined in the corresponding
|
||||
* catalog.
|
||||
* \param inIndex The index of the string to remove from the catalog list
|
||||
* \b GLC_CATALOG_LIST
|
||||
* \sa glcAppendCatalog()
|
||||
* \sa glcPrependCatalog()
|
||||
*/
|
||||
void APIENTRY glcRemoveCatalog(GLint inIndex)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Verify that the thread owns a context */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Verify that the parameter inIndex is in legal bounds */
|
||||
if (inIndex < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
__glcContextRemoveCatalog(ctx, inIndex);
|
||||
}
|
|
@ -0,0 +1,879 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the so-called "Measurement commands" described in chapter 3.10 of
|
||||
* the GLC specs.
|
||||
*/
|
||||
|
||||
/** \defgroup measure Measurement commands
|
||||
* Those commands returns metrics (bounding box, baseline) of character or
|
||||
* string layouts. Glyphs coordinates are defined in <em>em units</em> and are
|
||||
* transformed during rendering to produce the desired mapping of the glyph
|
||||
* shape into the GL window coordinate system. Moreover, GLC can return some
|
||||
* metrics for a character and string layouts. The table below lists the
|
||||
* metrics that are available :
|
||||
* <center>
|
||||
* <table>
|
||||
* <caption>Metrics for character and string layout</caption>
|
||||
* <tr>
|
||||
* <td>Name</td> <td>Enumerant</td> <td>Vector</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_BASELINE</b></td> <td>0x0030</td>
|
||||
* <td>[ x<sub>l</sub> y<sub>l</sub> x<sub>r</sub> y<sub>r</sub> ]</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_BOUNDS</b></td> <td>0x0031</td>
|
||||
* <td>[ x<sub>lb</sub> y<sub>lb</sub> x<sub>rb</sub> y<sub>rb</sub>
|
||||
* x<sub>rt</sub> y<sub>rt</sub> x<sub>lt</sub> y<sub>lt</sub> ]</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </center>
|
||||
* \n \b GLC_BASELINE is the line segment from the origin of the layout to the
|
||||
* origin of the following layout. \b GLC_BOUNDS is the bounding box of the
|
||||
* layout.
|
||||
*
|
||||
* \image html measure.png "Baseline and bounds"
|
||||
* \image latex measure.eps "Baseline and bounds" width=7cm
|
||||
* \n Each point <em>(x,y)</em> is computed in em coordinates, with the origin
|
||||
* of a layout at <em>(0,0)</em>. If the value of the variable
|
||||
* \b GLC_RENDER_STYLE is \b GLC_BITMAP or GLC_PIXMAP_QSO, each point is
|
||||
* transformed by the 2x2 \b GLC_BITMAP_MATRIX.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <math.h>
|
||||
|
||||
|
||||
|
||||
/* Multiply a vector by the GLC_BITMAP_MATRIX */
|
||||
static void __glcTransformVector(GLfloat* outVec, const GLfloat *inMatrix)
|
||||
{
|
||||
GLfloat temp = inMatrix[0] * outVec[0] + inMatrix[2] * outVec[1];
|
||||
|
||||
outVec[1] = inMatrix[1] * outVec[0] + inMatrix[3] * outVec[1];
|
||||
outVec[0] = temp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Retrieve the metrics of a character identified by 'inCode' in a font
|
||||
* identified by 'inFont'.
|
||||
* 'inCode' must be given in UCS-4 format
|
||||
*/
|
||||
static void* __glcGetCharMetric(const GLint inCode, const GLint inPrevCode,
|
||||
const GLboolean inIsRTL,
|
||||
const __GLCfont* inFont,
|
||||
__GLCcontext* inContext, const void* inData,
|
||||
const GLboolean inMultipleChars)
|
||||
{
|
||||
GLfloat* outVec = (GLfloat*)inData;
|
||||
int i = 0;
|
||||
GLfloat xMin = 0., xMax = 0.;
|
||||
GLfloat yMin = 0., yMax = 0.;
|
||||
GLfloat inScaleX = GLC_POINT_SIZE;
|
||||
GLfloat inScaleY = GLC_POINT_SIZE;
|
||||
GLfloat temp[4];
|
||||
|
||||
assert(inFont);
|
||||
|
||||
|
||||
if (inMultipleChars && ((inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO))) {
|
||||
/* If a string (or several characters) is to be measured, it will be easier
|
||||
* to perform the calculations in the glyph coordinate system than in the
|
||||
* screen coordinate system. In order to get the values that already stored
|
||||
* in outVec back in the glyph coordinate system, we must compute the
|
||||
* inverse of the transformation matrix.
|
||||
*/
|
||||
GLfloat* matrix = inContext->bitmapMatrix;
|
||||
GLfloat inverseMatrix[4];
|
||||
GLfloat norm = 0.f;
|
||||
GLfloat determinant = matrix[0] * matrix[3] - matrix[1] * matrix[2];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fabs(matrix[i]) > norm)
|
||||
norm = fabs(matrix[i]);
|
||||
}
|
||||
|
||||
if (determinant >= norm * GLC_EPSILON) {
|
||||
inverseMatrix[0] = matrix[3] / determinant;
|
||||
inverseMatrix[1] = -matrix[1] / determinant;
|
||||
inverseMatrix[2] = -matrix[2] / determinant;
|
||||
inverseMatrix[3] = matrix[0] / determinant;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
|
||||
/* Transform the values in outVec from the screen coordinate system to the
|
||||
* the glyph coordinate system
|
||||
*/
|
||||
for (i = 0; i < 7; i++)
|
||||
__glcTransformVector(&outVec[2*i], inverseMatrix);
|
||||
}
|
||||
|
||||
if (!inMultipleChars) {
|
||||
outVec[0] = 0.;
|
||||
outVec[1] = 0.;
|
||||
outVec[2] = 0.;
|
||||
outVec[3] = 0.;
|
||||
}
|
||||
else {
|
||||
outVec[2] += outVec[12];
|
||||
outVec[3] += outVec[13];
|
||||
}
|
||||
|
||||
if (!__glcFontGetBoundingBox(inFont, inCode, temp, inContext, inScaleX,
|
||||
inScaleY))
|
||||
return NULL;
|
||||
/* Take into account the advance of the glyphs that have already been
|
||||
* measured.
|
||||
*/
|
||||
xMin = temp[0] + outVec[2];
|
||||
yMin = temp[1] + outVec[3];
|
||||
xMax = temp[2] + outVec[2];
|
||||
yMax = temp[3] + outVec[3];
|
||||
|
||||
/* Update the global bounding box */
|
||||
if (inMultipleChars) {
|
||||
outVec[4] = xMin < outVec[4] ? xMin : outVec[4];
|
||||
outVec[5] = yMin < outVec[5] ? yMin : outVec[5];
|
||||
outVec[6] = xMax > outVec[6] ? xMax : outVec[6];
|
||||
outVec[9] = yMax > outVec[9] ? yMax : outVec[9];
|
||||
}
|
||||
else {
|
||||
outVec[4] = xMin;
|
||||
outVec[5] = yMin;
|
||||
outVec[6] = xMax;
|
||||
outVec[9] = yMax;
|
||||
}
|
||||
/* Finalize the update of the bounding box coordinates */
|
||||
outVec[7] = outVec[5];
|
||||
outVec[8] = outVec[6];
|
||||
outVec[10] = outVec[4];
|
||||
outVec[11] = outVec[9];
|
||||
|
||||
/* Get the advance of the glyph */
|
||||
if (!__glcFontGetAdvance(inFont, inCode, temp, inContext, inScaleX, inScaleY))
|
||||
return NULL;
|
||||
/* Update the global advance accordingly */
|
||||
if (inIsRTL) {
|
||||
outVec[2] -= temp[0];
|
||||
outVec[3] -= temp[1];
|
||||
}
|
||||
else {
|
||||
outVec[2] += temp[0];
|
||||
outVec[3] += temp[1];
|
||||
}
|
||||
|
||||
outVec[12] = 0.;
|
||||
outVec[13] = 0.;
|
||||
if (inPrevCode && inContext->enableState.kerning) {
|
||||
GLfloat kerning[2];
|
||||
const GLint leftCode = inIsRTL ? inCode : inPrevCode;
|
||||
const GLint rightCode = inIsRTL ? inPrevCode : inCode;
|
||||
|
||||
if (__glcFontGetKerning(inFont, leftCode, rightCode, kerning, inContext,
|
||||
inScaleX, inScaleY)) {
|
||||
outVec[12] = inIsRTL ? -kerning[0] : kerning[0];
|
||||
outVec[13] = kerning[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Transforms the values into the screen coordinate system if necessary */
|
||||
if ((inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO)){
|
||||
for (i = 0; i < 7; i++)
|
||||
__glcTransformVector(&outVec[2*i], inContext->bitmapMatrix);
|
||||
}
|
||||
|
||||
return outVec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup measure
|
||||
* This command is identical to the command glcRenderChar(), except that
|
||||
* instead of rendering the character that \e inCode is mapped to, the command
|
||||
* measures the resulting layout and stores in \e outVec the value of the
|
||||
* metric identified by \e inMetric. If the command does not raise an error,
|
||||
* its return value is \e outVec.
|
||||
*
|
||||
* \param inCode The character to measure.
|
||||
* \param inMetric The metric to measure, either \b GLC_BASELINE or
|
||||
* \b GLC_BOUNDS.
|
||||
* \param outVec A vector in which to store value of \e inMetric for specified
|
||||
* character.
|
||||
* \returns \e outVec if the command succeeds, \b NULL otherwise.
|
||||
* \sa glcGetMaxCharMetric()
|
||||
* \sa glcGetStringCharMetric()
|
||||
* \sa glcMeasureCountedString()
|
||||
* \sa glcMeasureString()
|
||||
*/
|
||||
GLfloat* APIENTRY glcGetCharMetric(GLint inCode, GLCenum inMetric,
|
||||
GLfloat *outVec)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLint code = 0;
|
||||
GLfloat vector[14];
|
||||
__GLCcharacter prevCode = { 0, NULL, NULL, {0.f, 0.f}};
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
assert(outVec);
|
||||
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
case GLC_BOUNDS:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verify if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the character code converted to the UCS-4 format */
|
||||
code = __glcConvertGLintToUcs4(ctx, inCode);
|
||||
if (code < 0)
|
||||
return NULL;
|
||||
|
||||
/* Control characters have no metrics */
|
||||
if (code < 32) {
|
||||
memset(outVec, 0, ((inMetric == GLC_BOUNDS) ? 8 : 4) * sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
|
||||
/* Call __glcProcessChar that will get a font which maps the code to a glyph
|
||||
* or issue the replacement code or the character sequence \<xxx> and call
|
||||
* __glcGetCharMetric()
|
||||
*/
|
||||
memset(vector, 0, 14 * sizeof(GLfloat));
|
||||
|
||||
if (__glcProcessChar(ctx, code, &prevCode, GL_FALSE, __glcGetCharMetric,
|
||||
vector)) {
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
memcpy(outVec, vector, 4 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
case GLC_BOUNDS:
|
||||
memcpy(outVec, &vector[4], 8 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup measure
|
||||
* This command measures the layout that would result from rendering all
|
||||
* mapped characters at the same origin. This contrast with
|
||||
* glcGetStringCharMetric(), which measures characters as part of a string,
|
||||
* that is, influenced by kerning, ligatures, and so on.
|
||||
*
|
||||
* This command evaluates the metrics of every fonts in the
|
||||
* \b GLC_CURRENT_FONT_LIST. Fonts that are not listed in
|
||||
* \b GLC_CURRENT_FONT_LIST are ignored.
|
||||
*
|
||||
* The command stores in \e outVec the value of the metric identified by
|
||||
* \e inMetric. If the command does not raise an error, its return value
|
||||
* is \e outVec.
|
||||
*
|
||||
* \param inMetric The metric to measure, either \b GLC_BASELINE or
|
||||
* \b GLC_BOUNDS.
|
||||
* \param outVec A vector in which to store value of \e inMetric for all
|
||||
* mapped character.
|
||||
* \returns \e outVec if the command succeeds, \b NULL otherwise.
|
||||
* \sa glcGetCharMetric()
|
||||
* \sa glcGetStringCharMetric()
|
||||
* \sa glcMeasureCountedString()
|
||||
* \sa glcMeasureString()
|
||||
*/
|
||||
GLfloat* APIENTRY glcGetMaxCharMetric(GLCenum inMetric, GLfloat *outVec)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLfloat advanceX = 0.f, advanceY = 0.f, yb = 1e4f, yt = -1e4f, xr = -1e4f,
|
||||
xl = 1e4f;
|
||||
FT_ListNode node = NULL;
|
||||
GLfloat inScaleX = GLC_POINT_SIZE;
|
||||
GLfloat inScaleY = GLC_POINT_SIZE;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
assert(outVec);
|
||||
|
||||
/* Check the parameters */
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
case GLC_BOUNDS:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verify if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* For each font in GLC_CURRENT_FONT_LIST find the maximum values of the
|
||||
* advance width of the bounding boxes.
|
||||
*/
|
||||
for (node = ctx->currentFontList.head; node; node = node->next) {
|
||||
GLfloat temp[6];
|
||||
__GLCfont* font = (__GLCfont*)node->data;
|
||||
|
||||
if (!__glcFontGetMaxMetric(font, temp, ctx, inScaleX, inScaleY))
|
||||
return NULL;
|
||||
|
||||
advanceX = temp[0] > advanceX ? temp[0] : advanceX;
|
||||
advanceY = temp[1] > advanceY ? temp[1] : advanceY;
|
||||
yt = temp[2] > yt ? temp[2] : yt;
|
||||
yb = temp[3] < yb ? temp[3] : yb;
|
||||
xr = temp[4] > xr ? temp[4] : xr;
|
||||
xl = temp[5] < xl ? temp[5] : xl;
|
||||
}
|
||||
|
||||
/* Update and transform, if necessary, the returned value */
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
outVec[0] = 0.;
|
||||
outVec[1] = 0.;
|
||||
outVec[2] = advanceX;
|
||||
outVec[3] = advanceY;
|
||||
if ((ctx->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (ctx->renderState.renderStyle == GLC_PIXMAP_QSO))
|
||||
__glcTransformVector(&outVec[2], ctx->bitmapMatrix);
|
||||
return outVec;
|
||||
case GLC_BOUNDS:
|
||||
outVec[0] = xl;
|
||||
outVec[1] = yb;
|
||||
outVec[2] = xr;
|
||||
outVec[3] = yb;
|
||||
outVec[4] = xr;
|
||||
outVec[5] = yt;
|
||||
outVec[6] = xl;
|
||||
outVec[7] = yt;
|
||||
if ((ctx->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (ctx->renderState.renderStyle == GLC_PIXMAP_QSO)) {
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
__glcTransformVector(&outVec[2*i], ctx->bitmapMatrix);
|
||||
}
|
||||
return outVec;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup measure
|
||||
* This command retrieves a character metric from the GLC measurement buffer
|
||||
* and stores it in \e outVec. To store a string in the measurement buffer,
|
||||
* call glcMeasureCountedString() or glcMeasureString().
|
||||
*
|
||||
* The character is identified by \e inIndex, and the metric is identified by
|
||||
* \e inMetric.
|
||||
*
|
||||
* The command raises \b GLC_PARAMETER_ERROR if \e inIndex is less than zero
|
||||
* or is greater than or equal to the value of the variable
|
||||
* \b GLC_MEASURED_CHAR_COUNT or \e outVec is NULL. If the command does not
|
||||
* raise an error, its return value is outVec.
|
||||
* \par Example:
|
||||
* The following example first calls glcMeasureString() to store the string
|
||||
* "hello" in the measurement buffer. It then retrieves both the baseline and
|
||||
* the bounding box for the whole string, then for each individual character.
|
||||
*
|
||||
* \code
|
||||
* GLfloat overallBaseline[4];
|
||||
* GLfloat overallBoundingBox[8];
|
||||
*
|
||||
* GLfloat charBaselines[5][4];
|
||||
* GLfloat charBoundingBoxes[5][8];
|
||||
*
|
||||
* GLint i;
|
||||
*
|
||||
* glcMeasureString(GL_TRUE, "hello");
|
||||
*
|
||||
* glcGetStringMetric(GLC_BASELINE, overallBaseline);
|
||||
* glcGetStringMetric(GLC_BOUNDS, overallBoundingBox);
|
||||
*
|
||||
* for (i = 0; i < 5; i++) {
|
||||
* glcGetStringCharMetric(i, GLC_BASELINE, charBaselines[i]);
|
||||
* glcGetStringCharMetric(i, GLC_BOUNDS, charBoundingBoxes[i]);
|
||||
* }
|
||||
* \endcode
|
||||
* \note
|
||||
* \e glcGetStringCharMetric is useful if you're interested in the metrics of
|
||||
* a character as it appears in a string, that is, influenced by kerning,
|
||||
* ligatures, and so on. To measure the metrics of a character alone, call
|
||||
* glcGetCharMetric().
|
||||
* \param inIndex Specifies which element in the string to measure.
|
||||
* \param inMetric The metric to measure, either \b GLC_BASELINE or
|
||||
* \b GLC_BOUNDS.
|
||||
* \param outVec A vector in which to store value of \e inMetric for the
|
||||
* character identified by \e inIndex.
|
||||
* \returns \e outVec if the command succeeds, \b NULL otherwise.
|
||||
* \sa glcGetCharMetric()
|
||||
* \sa glcGetMaxCharMetric()
|
||||
* \sa glcMeasureCountedString()
|
||||
* \sa glcMeasureString()
|
||||
*/
|
||||
GLfloat* APIENTRY glcGetStringCharMetric(GLint inIndex, GLCenum inMetric,
|
||||
GLfloat *outVec)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLfloat (*measurementBuffer)[12] = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
assert(outVec);
|
||||
|
||||
/* Check the parameters */
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
case GLC_BOUNDS:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verify if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
measurementBuffer = (GLfloat(*)[12])GLC_ARRAY_DATA(ctx->measurementBuffer);
|
||||
|
||||
/* Verify that inIndex is in legal bounds */
|
||||
if ((inIndex < 0)
|
||||
|| (inIndex >= GLC_ARRAY_LENGTH(ctx->measurementBuffer))) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
memcpy(outVec, &measurementBuffer[inIndex][0],
|
||||
4 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
case GLC_BOUNDS:
|
||||
memcpy(outVec, &measurementBuffer[inIndex][4],
|
||||
8 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup measure
|
||||
* This command retrieves a string metric from the GLC measurement buffer
|
||||
* and stores it in \e outVec. The metric is identified by \e inMetric. To
|
||||
* store the metrics of a string in the GLC measurement buffer, call
|
||||
* glcMeasureCountedString() or glcMeasureString().
|
||||
*
|
||||
* If the command does not raise an error, its return value is \e outVec.
|
||||
* \param inMetric The metric to measure, either \b GLC_BASELINE or
|
||||
* \b GLC_BOUNDS.
|
||||
* \param outVec A vector in which to store value of \e inMetric for the
|
||||
* character identified by \e inIndex.
|
||||
* \returns \e outVec if the command succeeds, \b NULL otherwise.
|
||||
* \sa glcGetCharMetric()
|
||||
* \sa glcGetMaxCharMetric()
|
||||
* \sa glcGetStringCharMetric()
|
||||
* \sa glcMeasureCountedString()
|
||||
* \sa glcMeasureString()
|
||||
*/
|
||||
GLfloat* APIENTRY glcGetStringMetric(GLCenum inMetric, GLfloat *outVec)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
assert(outVec);
|
||||
|
||||
/* Check the parameters */
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
case GLC_BOUNDS:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verify if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy the values requested by the client in outVec */
|
||||
switch(inMetric) {
|
||||
case GLC_BASELINE:
|
||||
memcpy(outVec, ctx->measurementStringBuffer, 4*sizeof(GLfloat));
|
||||
return outVec;
|
||||
case GLC_BOUNDS:
|
||||
memcpy(outVec, &ctx->measurementStringBuffer[4], 8*sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function perform the actual work of measuring a string
|
||||
* It is called by both glcMeasureString() and glcMeasureCountedString()
|
||||
* The string inString is encoded in UCS4 and is stored in visual order.
|
||||
*/
|
||||
static GLint __glcMeasureCountedString(__GLCcontext *inContext,
|
||||
const GLboolean inMeasureChars,
|
||||
const GLint inCount,
|
||||
const GLCchar32* inString,
|
||||
const GLboolean inIsRTL)
|
||||
{
|
||||
GLint i = 0;
|
||||
GLfloat metrics[14];
|
||||
const GLCchar32* ptr = NULL;
|
||||
const GLint storeRenderStyle = inContext->renderState.renderStyle;
|
||||
GLfloat xMin = 0., xMax = 0.;
|
||||
GLfloat yMin = 0., yMax = 0.;
|
||||
GLfloat* outVec = inContext->measurementStringBuffer;
|
||||
__GLCcharacter prevCode = { 0, NULL, NULL, {0.f, 0.f}};
|
||||
GLint shift = 1;
|
||||
|
||||
if ((inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO)) {
|
||||
/* In order to prevent __glcProcessCharMetric() to transform its results
|
||||
* with the GLC_MATRIX, ctx->renderStyle must not be GLC_BITMAP (or
|
||||
* GLC_PIXMAP_QSO)
|
||||
*/
|
||||
inContext->renderState.renderStyle = 0;
|
||||
}
|
||||
|
||||
memset(outVec, 0, 12*sizeof(GLfloat));
|
||||
|
||||
if (inMeasureChars)
|
||||
GLC_ARRAY_LENGTH(inContext->measurementBuffer) = 0;
|
||||
|
||||
/* For each character of the string, the measurement are performed and
|
||||
* gathered in the context state
|
||||
*/
|
||||
ptr = inString;
|
||||
if (inIsRTL) {
|
||||
ptr += inCount - 1;
|
||||
shift = -1;
|
||||
}
|
||||
|
||||
memset(metrics, 0, 14 * sizeof(GLfloat));
|
||||
|
||||
for (i = 0; i < inCount; i++) {
|
||||
if (*ptr < 32) {
|
||||
/* Control characters have no metrics. However they must not be skipped
|
||||
* otherwise the characters indices in the string would be modified and
|
||||
* this would make troubles when the user calls glcGetStringCharMetric().
|
||||
*/
|
||||
memset(metrics, 0, 14 * sizeof(GLfloat));
|
||||
}
|
||||
else {
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
if (inContext->enableState.glObjects
|
||||
&& inContext->renderState.renderStyle) {
|
||||
__GLCfont* font = NULL;
|
||||
__GLCglyph* glyph = NULL;
|
||||
|
||||
for (node = inContext->currentFontList.head; node ; node = node->next) {
|
||||
font = (__GLCfont*)node->data;
|
||||
glyph = __glcCharMapGetGlyph(font->charMap, *ptr);
|
||||
|
||||
metrics[0] = 0.;
|
||||
metrics[1] = 0.;
|
||||
|
||||
if (!glyph || !glyph->advanceCached) {
|
||||
if (!__glcFontGetAdvance(font, *ptr, &metrics[2], inContext,
|
||||
GLC_POINT_SIZE, GLC_POINT_SIZE))
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
metrics[2] = glyph->advance[0];
|
||||
metrics[3] = glyph->advance[1];
|
||||
}
|
||||
|
||||
if (!glyph || !glyph->boundingBoxCached) {
|
||||
if (!__glcFontGetBoundingBox(font, *ptr, &metrics[4], inContext,
|
||||
GLC_POINT_SIZE, GLC_POINT_SIZE))
|
||||
continue;
|
||||
metrics[9] = metrics[7];
|
||||
}
|
||||
else {
|
||||
metrics[4] = glyph->boundingBox[0];
|
||||
metrics[5] = glyph->boundingBox[1];
|
||||
metrics[6] = glyph->boundingBox[2];
|
||||
metrics[9] = glyph->boundingBox[3];
|
||||
}
|
||||
|
||||
metrics[7] = metrics[5];
|
||||
metrics[8] = metrics[6];
|
||||
metrics[10] = metrics[4];
|
||||
metrics[11] = metrics[9];
|
||||
|
||||
if (inContext->enableState.kerning) {
|
||||
if (prevCode.code && prevCode.font == font) {
|
||||
const GLint leftCode = inIsRTL ? *ptr : prevCode.code;
|
||||
const GLint rightCode = inIsRTL ? prevCode.code : *ptr;
|
||||
|
||||
if (!__glcFontGetKerning(font, leftCode, rightCode, &metrics[12],
|
||||
inContext, GLC_POINT_SIZE,
|
||||
GLC_POINT_SIZE))
|
||||
memset(&metrics[12], 0, 2*sizeof(GLfloat));
|
||||
}
|
||||
}
|
||||
|
||||
prevCode.font = font;
|
||||
prevCode.code = *ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
__glcProcessChar(inContext, *ptr, &prevCode, inIsRTL,
|
||||
__glcGetCharMetric, metrics);
|
||||
}
|
||||
}
|
||||
|
||||
ptr += shift;
|
||||
|
||||
/* If characters are to be measured then store the results */
|
||||
if (inMeasureChars) {
|
||||
__glcArrayAppend(inContext->measurementBuffer, metrics);
|
||||
|
||||
if (i) {
|
||||
GLfloat (*measurementBuffer)[12] =
|
||||
(GLfloat(*)[12])GLC_ARRAY_DATA(inContext->measurementBuffer);
|
||||
GLfloat prevCharAdvance = measurementBuffer[i-1][2] + metrics[12];
|
||||
int j = 0;
|
||||
|
||||
for (j = 0; j < 6; j++)
|
||||
measurementBuffer[i][2*j] += prevCharAdvance;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize outVec if we are processing the first character of the string
|
||||
*/
|
||||
if (!i) {
|
||||
outVec[0] = metrics[0];
|
||||
outVec[1] = metrics[1];
|
||||
outVec[2] = metrics[0];
|
||||
outVec[3] = metrics[1];
|
||||
outVec[4] = metrics[4] + metrics[0];
|
||||
outVec[5] = metrics[5] + metrics[1];
|
||||
outVec[6] = metrics[6] + metrics[0];
|
||||
outVec[9] = metrics[9] + metrics[1];
|
||||
}
|
||||
else {
|
||||
/* Takes the kerning into account */
|
||||
outVec[2] += metrics[12];
|
||||
outVec[3] += metrics[13];
|
||||
}
|
||||
|
||||
xMin = metrics[4] + outVec[2];
|
||||
xMax = metrics[6] + outVec[2];
|
||||
yMin = metrics[5] + outVec[3];
|
||||
yMax = metrics[9] + outVec[3];
|
||||
|
||||
outVec[4] = xMin < outVec[4] ? xMin : outVec[4];
|
||||
outVec[5] = yMin < outVec[5] ? yMin : outVec[5];
|
||||
outVec[6] = xMax > outVec[6] ? xMax : outVec[6];
|
||||
outVec[9] = yMax > outVec[9] ? yMax : outVec[9];
|
||||
|
||||
outVec[2] += metrics[2];
|
||||
outVec[3] += metrics[3];
|
||||
}
|
||||
|
||||
outVec[7] = outVec[5];
|
||||
outVec[8] = outVec[6];
|
||||
outVec[10] = outVec[4];
|
||||
outVec[11] = outVec[9];
|
||||
|
||||
/* Transform all the data in the screen coordinate system if the rendering
|
||||
* style is GLC_BITMAP or GLC_PIXMAP_QSO.
|
||||
*/
|
||||
if ((storeRenderStyle == GLC_BITMAP)
|
||||
|| (storeRenderStyle == GLC_PIXMAP_QSO)) {
|
||||
inContext->renderState.renderStyle = storeRenderStyle;
|
||||
for (i = 0; i < 6; i++)
|
||||
__glcTransformVector(&inContext->measurementStringBuffer[2*i],
|
||||
inContext->bitmapMatrix);
|
||||
if (inMeasureChars) {
|
||||
GLfloat (*measurementBuffer)[12] =
|
||||
(GLfloat(*)[12])GLC_ARRAY_DATA(inContext->measurementBuffer);
|
||||
int j = 0;
|
||||
|
||||
for (i = 0; i < inCount; i++) {
|
||||
for (j = 0; j < 6; j++)
|
||||
__glcTransformVector(&measurementBuffer[i][2*j],
|
||||
inContext->bitmapMatrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the number of measured characters */
|
||||
return inCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup measure
|
||||
* This command is identical to the command glcRenderCountedString(), except
|
||||
* that instead of rendering a string, the command measures the resulting
|
||||
* layout and stores the measurement in the GLC measurement buffer. The
|
||||
* string comprises the first \e inCount elements of the array \e inString,
|
||||
* which need not be followed by a zero element.
|
||||
*
|
||||
* If the value \e inMeasureChars is nonzero, the command computes metrics for
|
||||
* each character and for the overall string, and it assigns the value
|
||||
* \e inCount to the variable \b GLC_MEASURED_CHARACTER_COUNT. Otherwise, the
|
||||
* command computes metrics only for the overall string, and it assigns the
|
||||
* value zero to the variable \b GLC_MEASURED_CHARACTER_COUNT.
|
||||
*
|
||||
* If the command does not raise an error, its return value is the value of
|
||||
* the variable \b GLC_MEASURED_CHARACTER_COUNT.
|
||||
*
|
||||
* The command raises \b GLC_PARAMETER_ERROR if \e inCount is less than zero.
|
||||
* \param inMeasureChars Specifies whether to compute metrics only for the
|
||||
* string or for the characters as well.
|
||||
* \param inCount The number of elements to measure, starting at the first
|
||||
* element.
|
||||
* \param inString The string to be measured.
|
||||
* \returns The variable \b GLC_MEASURED_CHARACTER_COUNT if the command
|
||||
* succeeds, zero otherwise.
|
||||
* \sa glcGeti() with argument GLC_MEASURED_CHAR_COUNT
|
||||
* \sa glcGetStringCharMetric()
|
||||
* \sa glcGetStringMetric()
|
||||
*/
|
||||
GLint APIENTRY glcMeasureCountedString(GLboolean inMeasureChars, GLint inCount,
|
||||
const GLCchar* inString)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLint count = 0;
|
||||
GLCchar32* UinString = NULL;
|
||||
GLboolean isRightToLeft = GL_FALSE;
|
||||
|
||||
/* If inString is NULL then there is no point in continuing */
|
||||
if (!inString)
|
||||
return 0;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check the parameters */
|
||||
if (inCount < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Verify if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
UinString = __glcConvertCountedStringToVisualUcs4(ctx, &isRightToLeft,
|
||||
inString, inCount);
|
||||
if (!UinString)
|
||||
return 0;
|
||||
|
||||
count = __glcMeasureCountedString(ctx, inMeasureChars, inCount, UinString,
|
||||
isRightToLeft);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup measure
|
||||
* This command measures the layout that would result from rendering a string
|
||||
* and stores the measurements in the GLC measurement buffer. This command
|
||||
* is identical to the command glcMeasureCountedString(), except that
|
||||
* \e inString is zero terminated, not counted.
|
||||
*
|
||||
* If the command does not raise an error, its return value is the value of
|
||||
* the variable \b GLC_MEASURED_CHARACTER_COUNT.
|
||||
* \param inMeasureChars Specifies whether to compute metrics only for the
|
||||
* string or for the characters as well.
|
||||
* \param inString The string to be measured.
|
||||
* \returns The variable \b GLC_MEASURED_CHARACTER_COUNT if the command
|
||||
* succeeds, zero otherwise.
|
||||
* \sa glcGeti() with argument GLC_MEASURED_CHAR_COUNT
|
||||
* \sa glcGetStringCharMetric()
|
||||
* \sa glcGetStringMetric()
|
||||
*/
|
||||
GLint APIENTRY glcMeasureString(GLboolean inMeasureChars,
|
||||
const GLCchar* inString)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLCchar32* UinString = NULL;
|
||||
GLint count = 0;
|
||||
GLint length = 0;
|
||||
GLboolean isRightToLeft = GL_FALSE;
|
||||
|
||||
/* If inString is NULL then there is no point in continuing */
|
||||
if (!inString)
|
||||
return 0;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Verify if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
UinString = __glcConvertToVisualUcs4(ctx, &isRightToLeft, &length, inString);
|
||||
if (!UinString)
|
||||
return 0;
|
||||
|
||||
count = __glcMeasureCountedString(ctx, inMeasureChars, length, UinString,
|
||||
isRightToLeft);
|
||||
|
||||
return count;
|
||||
}
|
|
@ -0,0 +1,659 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines miscellaneous utility routines used throughout QuesoGLC.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
GLuint __glcMemAllocCount = 0;
|
||||
GLuint __glcMemAllocTrigger = 0;
|
||||
GLboolean __glcMemAllocFailOnce = GL_TRUE;
|
||||
|
||||
|
||||
/* QuesoGLC own allocation and memory management routines */
|
||||
void* __glcMalloc(size_t size)
|
||||
{
|
||||
__glcMemAllocCount++;
|
||||
|
||||
if (__glcMemAllocFailOnce) {
|
||||
if (__glcMemAllocCount == __glcMemAllocTrigger)
|
||||
return NULL;
|
||||
}
|
||||
else if (__glcMemAllocCount >= __glcMemAllocTrigger)
|
||||
return NULL;
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void __glcFree(void *ptr)
|
||||
{
|
||||
/* Not all implementations of free() accept NULL. Moreover this allows to
|
||||
* detect useless calls.
|
||||
*/
|
||||
assert(ptr);
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void* __glcRealloc(void *ptr, size_t size)
|
||||
{
|
||||
__glcMemAllocCount++;
|
||||
|
||||
if (__glcMemAllocFailOnce) {
|
||||
if (__glcMemAllocCount == __glcMemAllocTrigger)
|
||||
return NULL;
|
||||
}
|
||||
else if (__glcMemAllocCount >= __glcMemAllocTrigger)
|
||||
return NULL;
|
||||
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef HAVE_TLS
|
||||
/* Each thread has to store specific informations so they can be retrieved
|
||||
* later. __glcGetThreadArea() returns a struct which contains thread specific
|
||||
* info for GLC.
|
||||
* If the '__GLCthreadArea' of the current thread does not exist, it is created
|
||||
* and initialized.
|
||||
* IMPORTANT NOTE : __glcGetThreadArea() must never use __glcMalloc() and
|
||||
* __glcFree() since those functions could use the exceptContextStack
|
||||
* before it is initialized.
|
||||
*/
|
||||
__GLCthreadArea* __glcGetThreadArea(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
|
||||
#ifdef __WIN32__
|
||||
area = (__GLCthreadArea*)TlsGetValue(__glcCommonArea.threadKey);
|
||||
#else
|
||||
area = (__GLCthreadArea*)pthread_getspecific(__glcCommonArea.threadKey);
|
||||
#endif
|
||||
if (area)
|
||||
return area;
|
||||
|
||||
area = (__GLCthreadArea*)malloc(sizeof(__GLCthreadArea));
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
area->currentContext = NULL;
|
||||
area->errorState = GLC_NONE;
|
||||
area->lockState = 0;
|
||||
area->exceptionStack.head = NULL;
|
||||
area->exceptionStack.tail = NULL;
|
||||
area->failedTry = GLC_NO_EXC;
|
||||
#ifdef __WIN32__
|
||||
if (!TlsSetValue(__glcCommonArea.threadKey, (LPVOID)area)) {
|
||||
free(area);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
pthread_setspecific(__glcCommonArea.threadKey, (void*)area);
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
if (__glcCommonArea.threadID == GetCurrentThreadId())
|
||||
#else
|
||||
if (pthread_equal(__glcCommonArea.threadID, pthread_self()))
|
||||
#endif
|
||||
__glcThreadArea = area;
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Raise an error. This function must be called each time the current error
|
||||
* of the issuing thread must be set
|
||||
*/
|
||||
void __glcRaiseError(GLCenum inError)
|
||||
{
|
||||
GLCenum error = GLC_NONE;
|
||||
__GLCthreadArea *area = NULL;
|
||||
|
||||
area = GLC_GET_THREAD_AREA();
|
||||
assert(area);
|
||||
|
||||
/* An error can only be raised if the current error value is GLC_NONE.
|
||||
* However, when inError == GLC_NONE then we must force the current error
|
||||
* value to GLC_NONE whatever its previous value was.
|
||||
*/
|
||||
error = area->errorState;
|
||||
if (!error || !inError)
|
||||
area->errorState = inError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the current context of the issuing thread */
|
||||
__GLCcontext* __glcGetCurrent(void)
|
||||
{
|
||||
__GLCthreadArea *area = NULL;
|
||||
|
||||
area = __glcGetThreadArea(); /* Don't use GLC_GET_THREAD_AREA */
|
||||
assert(area);
|
||||
|
||||
return area->currentContext;
|
||||
}
|
||||
#endif /* HAVE_TLS */
|
||||
|
||||
|
||||
|
||||
/* Process the character in order to find a font that maps the code and to
|
||||
* render the corresponding glyph. Replacement code and '<hexcode>' format
|
||||
* are issued if necessary. The previous code is updated accordingly.
|
||||
* 'inCode' must be given in UCS-4 format
|
||||
*/
|
||||
void* __glcProcessChar(__GLCcontext *inContext, const GLint inCode,
|
||||
__GLCcharacter* inPrevCode, const GLboolean inIsRTL,
|
||||
const __glcProcessCharFunc inProcessCharFunc,
|
||||
const void* inProcessCharData)
|
||||
{
|
||||
GLint repCode = 0;
|
||||
__GLCfont* font = NULL;
|
||||
void* ret = NULL;
|
||||
|
||||
if (!inCode)
|
||||
return NULL;
|
||||
|
||||
/* Get a font that maps inCode */
|
||||
font = __glcContextGetFont(inContext, inCode);
|
||||
if (font) {
|
||||
/* A font has been found */
|
||||
if (font != inPrevCode->font)
|
||||
inPrevCode->code = 0; /* The font has changed, kerning must be disabled */
|
||||
ret = inProcessCharFunc(inCode, inPrevCode->code, inIsRTL, font, inContext,
|
||||
inProcessCharData, GL_FALSE);
|
||||
inPrevCode->code = inCode;
|
||||
inPrevCode->font = font;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* __glcContextGetFont() can not find a font that maps inCode, we then attempt
|
||||
* to produce an alternate rendering.
|
||||
*/
|
||||
|
||||
/* If the variable GLC_REPLACEMENT_CODE is nonzero, and __glcContextGetFont()
|
||||
* finds a font that maps the replacement code, we now render the character
|
||||
* that the replacement code is mapped to
|
||||
*/
|
||||
repCode = inContext->stringState.replacementCode;
|
||||
font = __glcContextGetFont(inContext, repCode);
|
||||
if (repCode && font) {
|
||||
if (font != inPrevCode->font)
|
||||
inPrevCode->code = 0; /* The font has changed, kerning must be disabled */
|
||||
ret = inProcessCharFunc(repCode, inPrevCode->code, inIsRTL, font, inContext,
|
||||
inProcessCharData, GL_FALSE);
|
||||
inPrevCode->code = repCode;
|
||||
inPrevCode->font = font;
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
/* If we get there, we failed to render both the character that inCode maps
|
||||
* to and the replacement code. Now, we will try to render the character
|
||||
* sequence "\<hexcode>", where '\' is the character REVERSE SOLIDUS
|
||||
* (U+5C), '<' is the character LESS-THAN SIGN (U+3C), '>' is the character
|
||||
* GREATER-THAN SIGN (U+3E), and 'hexcode' is inCode represented as a
|
||||
* sequence of hexadecimal digits. The sequence has no leading zeros, and
|
||||
* alphabetic digits are in upper case. The GLC measurement commands treat
|
||||
* the sequence as a single character.
|
||||
*/
|
||||
char buf[11];
|
||||
GLint i = 0;
|
||||
GLint n = 0;
|
||||
|
||||
/* Check if a font maps hexadecimal digits */
|
||||
#ifdef _MSC_VER
|
||||
n = sprintf_s(buf, 11, "\\<%X>", (int)inCode);
|
||||
if (n < 0) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
n = snprintf(buf, 11, "\\<%X>", (int)inCode);
|
||||
#endif
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!__glcContextGetFont(inContext, buf[i]))
|
||||
/* The code is not rendered, the previous code is thus left unchanged */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Render the '\<hexcode>' sequence */
|
||||
for (i = 0; i < n; i++) {
|
||||
GLint pos = inIsRTL ? n-i-1 : i;
|
||||
|
||||
font = __glcContextGetFont(inContext, buf[pos]);
|
||||
if (font != inPrevCode->font)
|
||||
inPrevCode->code = 0; /*The font has changed, kerning must be disabled*/
|
||||
ret = inProcessCharFunc(buf[pos], inPrevCode->code, inIsRTL, font,
|
||||
inContext, inProcessCharData, GL_TRUE);
|
||||
inPrevCode->code = buf[pos];
|
||||
inPrevCode->font = font;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Store an 4x4 identity matrix in 'm' */
|
||||
static void __glcMakeIdentity(GLfloat* m)
|
||||
{
|
||||
memset(m, 0, 16 * sizeof(GLfloat));
|
||||
m[0] = 1.f;
|
||||
m[5] = 1.f;
|
||||
m[10] = 1.f;
|
||||
m[15] = 1.f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Invert a 4x4 matrix stored in inMatrix. The result is stored in outMatrix
|
||||
* It uses the Gauss-Jordan elimination method
|
||||
*/
|
||||
static GLboolean __glcInvertMatrix(const GLfloat* inMatrix, GLfloat* outMatrix)
|
||||
{
|
||||
int i, j, k, swap;
|
||||
GLfloat t;
|
||||
GLfloat temp[4][4];
|
||||
|
||||
for (i=0; i<4; i++) {
|
||||
for (j=0; j<4; j++) {
|
||||
temp[i][j] = inMatrix[i*4+j];
|
||||
}
|
||||
}
|
||||
__glcMakeIdentity(outMatrix);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
/* Look for largest element in column */
|
||||
swap = i;
|
||||
for (j = i + 1; j < 4; j++) {
|
||||
if (fabs(temp[j][i]) > fabs(temp[i][i])) {
|
||||
swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if (swap != i) {
|
||||
/* Swap rows */
|
||||
for (k = 0; k < 4; k++) {
|
||||
t = temp[i][k];
|
||||
temp[i][k] = temp[swap][k];
|
||||
temp[swap][k] = t;
|
||||
|
||||
t = outMatrix[i*4+k];
|
||||
outMatrix[i*4+k] = outMatrix[swap*4+k];
|
||||
outMatrix[swap*4+k] = t;
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(temp[i][i]) < GLC_EPSILON) {
|
||||
/* No non-zero pivot. The matrix is singular, which shouldn't
|
||||
* happen. This means the user gave us a bad matrix.
|
||||
*/
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
t = temp[i][i];
|
||||
for (k = 0; k < 4; k++) {
|
||||
temp[i][k] /= t;
|
||||
outMatrix[i*4+k] /= t;
|
||||
}
|
||||
for (j = 0; j < 4; j++) {
|
||||
if (j != i) {
|
||||
t = temp[j][i];
|
||||
for (k = 0; k < 4; k++) {
|
||||
temp[j][k] -= temp[i][k]*t;
|
||||
outMatrix[j*4+k] -= outMatrix[i*4+k]*t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Mutiply two 4x4 matrices, the operands are stored in inMatrix1 and inMatrix2
|
||||
* The result is stored in outMatrix which can be neither inMatrix1 nor
|
||||
* inMatrix2.
|
||||
*/
|
||||
static void __glcMultMatrices(const GLfloat* inMatrix1,
|
||||
const GLfloat* inMatrix2, GLfloat* outMatrix)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
outMatrix[i*4+j] =
|
||||
inMatrix1[i*4+0]*inMatrix2[0*4+j] +
|
||||
inMatrix1[i*4+1]*inMatrix2[1*4+j] +
|
||||
inMatrix1[i*4+2]*inMatrix2[2*4+j] +
|
||||
inMatrix1[i*4+3]*inMatrix2[3*4+j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Compute an optimal size for the glyph to be rendered on the screen if no
|
||||
* display list is planned to be built.
|
||||
*/
|
||||
void __glcGetScale(const __GLCcontext* inContext, GLfloat* outTransformMatrix,
|
||||
GLfloat* outScaleX, GLfloat* outScaleY)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if ((inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)) {
|
||||
/* Compute the matrix that transforms object space coordinates to viewport
|
||||
* coordinates. If we plan to use object space coordinates, this matrix is
|
||||
* set to identity.
|
||||
*/
|
||||
GLfloat projectionMatrix[16];
|
||||
GLfloat modelviewMatrix[16];
|
||||
GLint viewport[4];
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
|
||||
glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);
|
||||
|
||||
__glcMultMatrices(modelviewMatrix, projectionMatrix, outTransformMatrix);
|
||||
|
||||
if (!inContext->enableState.glObjects && inContext->enableState.hinting) {
|
||||
GLfloat rs[16], m[16];
|
||||
/* Get the scale factors in each X, Y and Z direction */
|
||||
GLfloat sx = sqrt(outTransformMatrix[0] * outTransformMatrix[0]
|
||||
+outTransformMatrix[1] * outTransformMatrix[1]
|
||||
+outTransformMatrix[2] * outTransformMatrix[2]);
|
||||
GLfloat sy = sqrt(outTransformMatrix[4] * outTransformMatrix[4]
|
||||
+outTransformMatrix[5] * outTransformMatrix[5]
|
||||
+outTransformMatrix[6] * outTransformMatrix[6]);
|
||||
GLfloat sz = sqrt(outTransformMatrix[8] * outTransformMatrix[8]
|
||||
+outTransformMatrix[9] * outTransformMatrix[9]
|
||||
+outTransformMatrix[10] * outTransformMatrix[10]);
|
||||
GLfloat x = 0., y = 0.;
|
||||
|
||||
memset(rs, 0, 16 * sizeof(GLfloat));
|
||||
rs[15] = 1.;
|
||||
for (i = 0; i < 3; i++) {
|
||||
rs[0+4*i] = outTransformMatrix[0+4*i] / sx;
|
||||
rs[1+4*i] = outTransformMatrix[1+4*i] / sy;
|
||||
rs[2+4*i] = outTransformMatrix[2+4*i] / sz;
|
||||
}
|
||||
if (!__glcInvertMatrix(rs, rs)) {
|
||||
*outScaleX = 0.f;
|
||||
*outScaleY = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
__glcMultMatrices(rs, outTransformMatrix, m);
|
||||
x = ((m[0] + m[12])/(m[3] + m[15]) - m[12]/m[15]) * viewport[2] * 0.5;
|
||||
y = ((m[1] + m[13])/(m[3] + m[15]) - m[13]/m[15]) * viewport[3] * 0.5;
|
||||
*outScaleX = sqrt(x*x+y*y);
|
||||
x = ((m[4] + m[12])/(m[7] + m[15]) - m[12]/m[15]) * viewport[2] * 0.5;
|
||||
y = ((m[5] + m[13])/(m[7] + m[15]) - m[13]/m[15]) * viewport[3] * 0.5;
|
||||
*outScaleY = sqrt(x*x+y*y);
|
||||
}
|
||||
else {
|
||||
*outScaleX = GLC_POINT_SIZE;
|
||||
*outScaleY = GLC_POINT_SIZE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
GLfloat determinant = 0., norm = 0.;
|
||||
GLfloat *transform = inContext->bitmapMatrix;
|
||||
|
||||
/* Compute the norm of the transformation matrix */
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fabsf(transform[i]) > norm)
|
||||
norm = fabsf(transform[i]);
|
||||
}
|
||||
|
||||
determinant = transform[0] * transform[3] - transform[1] * transform[2];
|
||||
|
||||
/* If the transformation is degenerated, nothing needs to be rendered */
|
||||
if (fabsf(determinant) < norm * GLC_EPSILON) {
|
||||
*outScaleX = 0.f;
|
||||
*outScaleY = 0.f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (inContext->enableState.hinting) {
|
||||
*outScaleX = sqrt(transform[0]*transform[0]+transform[1]*transform[1]);
|
||||
*outScaleY = sqrt(transform[2]*transform[2]+transform[3]*transform[3]);
|
||||
}
|
||||
else {
|
||||
*outScaleX = GLC_POINT_SIZE;
|
||||
*outScaleY = GLC_POINT_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Save the GL State in a structure */
|
||||
void __glcSaveGLState(__GLCglState* inGLState, const __GLCcontext* inContext,
|
||||
const GLboolean inAll)
|
||||
{
|
||||
if (inAll || inContext->renderState.renderStyle == GLC_TEXTURE) {
|
||||
inGLState->blend = glIsEnabled(GL_BLEND);
|
||||
glGetIntegerv(GL_BLEND_SRC, &inGLState->blendSrc);
|
||||
glGetIntegerv(GL_BLEND_DST, &inGLState->blendDst);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &inGLState->textureID);
|
||||
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,
|
||||
&inGLState->textureEnvMode);
|
||||
if ((inAll || !inContext->enableState.glObjects)
|
||||
&& GLEW_ARB_pixel_buffer_object)
|
||||
glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING_ARB,
|
||||
&inGLState->pixelBufferObjectID);
|
||||
}
|
||||
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO)) {
|
||||
glGetIntegerv(GL_UNPACK_LSB_FIRST, &inGLState->unpackLsbFirst);
|
||||
glGetIntegerv(GL_UNPACK_ROW_LENGTH, &inGLState->unpackRowLength);
|
||||
glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &inGLState->unpackSkipPixels);
|
||||
glGetIntegerv(GL_UNPACK_SKIP_ROWS, &inGLState->unpackSkipRows);
|
||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &inGLState->unpackAlignment);
|
||||
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_PIXMAP_QSO)) {
|
||||
if (!inAll) { /* if inAll, already saved in GLC_TEXTURE's block */
|
||||
inGLState->blend = glIsEnabled(GL_BLEND);
|
||||
glGetIntegerv(GL_BLEND_SRC, &inGLState->blendSrc);
|
||||
glGetIntegerv(GL_BLEND_DST, &inGLState->blendDst);
|
||||
}
|
||||
glGetFloatv(GL_RED_BIAS, &inGLState->colorBias[0]);
|
||||
glGetFloatv(GL_GREEN_BIAS, &inGLState->colorBias[1]);
|
||||
glGetFloatv(GL_BLUE_BIAS, &inGLState->colorBias[2]);
|
||||
glGetFloatv(GL_ALPHA_BIAS, &inGLState->colorBias[3]);
|
||||
glGetFloatv(GL_RED_SCALE, &inGLState->colorScale[0]);
|
||||
glGetFloatv(GL_GREEN_SCALE, &inGLState->colorScale[1]);
|
||||
glGetFloatv(GL_BLUE_SCALE, &inGLState->colorScale[2]);
|
||||
glGetFloatv(GL_ALPHA_SCALE, &inGLState->colorScale[3]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((inAll || (inContext->enableState.glObjects
|
||||
&& (inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)))
|
||||
&& GLEW_ARB_vertex_buffer_object) {
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB,
|
||||
&inGLState->vertexBufferObjectID);
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_TRIANGLE))
|
||||
glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB,
|
||||
&inGLState->elementBufferObjectID);
|
||||
}
|
||||
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
&& inContext->enableState.glObjects
|
||||
&& inContext->enableState.extrude))
|
||||
inGLState->normalize = glIsEnabled(GL_NORMALIZE);
|
||||
|
||||
if (inAll || inContext->renderState.renderStyle == GLC_LINE
|
||||
|| inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
|| (inContext->renderState.renderStyle == GLC_TEXTURE
|
||||
&& inContext->enableState.glObjects
|
||||
&& GLEW_ARB_vertex_buffer_object)) {
|
||||
inGLState->vertexArray = glIsEnabled(GL_VERTEX_ARRAY);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_SIZE, &inGLState->vertexArraySize);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_TYPE, &inGLState->vertexArrayType);
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_STRIDE, &inGLState->vertexArrayStride);
|
||||
glGetPointerv(GL_VERTEX_ARRAY_POINTER, &inGLState->vertexArrayPointer);
|
||||
inGLState->normalArray = glIsEnabled(GL_NORMAL_ARRAY);
|
||||
inGLState->colorArray = glIsEnabled(GL_COLOR_ARRAY);
|
||||
inGLState->indexArray = glIsEnabled(GL_INDEX_ARRAY);
|
||||
inGLState->texCoordArray = glIsEnabled(GL_TEXTURE_COORD_ARRAY);
|
||||
if (inAll || inContext->renderState.renderStyle == GLC_TEXTURE) {
|
||||
glGetIntegerv(GL_TEXTURE_COORD_ARRAY_SIZE, &inGLState->texCoordArraySize);
|
||||
glGetIntegerv(GL_TEXTURE_COORD_ARRAY_TYPE, &inGLState->texCoordArrayType);
|
||||
glGetIntegerv(GL_TEXTURE_COORD_ARRAY_STRIDE,
|
||||
&inGLState->texCoordArrayStride);
|
||||
glGetPointerv(GL_TEXTURE_COORD_ARRAY_POINTER,
|
||||
&inGLState->texCoordArrayPointer);
|
||||
}
|
||||
inGLState->edgeFlagArray = glIsEnabled(GL_EDGE_FLAG_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Restore the GL State from a structure */
|
||||
void __glcRestoreGLState(const __GLCglState* inGLState,
|
||||
const __GLCcontext* inContext, const GLboolean inAll)
|
||||
{
|
||||
if (inAll || inContext->renderState.renderStyle == GLC_TEXTURE) {
|
||||
if (!inGLState->blend)
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(inGLState->blendSrc, inGLState->blendDst);
|
||||
glBindTexture(GL_TEXTURE_2D, inGLState->textureID);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, inGLState->textureEnvMode);
|
||||
if ((inAll || !inContext->enableState.glObjects)
|
||||
&& GLEW_ARB_pixel_buffer_object)
|
||||
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB,
|
||||
inGLState->pixelBufferObjectID);
|
||||
}
|
||||
|
||||
if ((inAll || (inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO))) {
|
||||
glPixelStorei(GL_UNPACK_LSB_FIRST, inGLState->unpackLsbFirst);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, inGLState->unpackRowLength);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, inGLState->unpackSkipPixels);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, inGLState->unpackSkipRows);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, inGLState->unpackAlignment);
|
||||
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_PIXMAP_QSO)) {
|
||||
if (!inAll) { /* if inAll, already restored in GLC_TEXTURE's block */
|
||||
if (!inGLState->blend)
|
||||
glDisable(GL_BLEND);
|
||||
glBlendFunc(inGLState->blendSrc, inGLState->blendDst);
|
||||
}
|
||||
glPixelTransferf(GL_RED_BIAS, inGLState->colorBias[0]);
|
||||
glPixelTransferf(GL_GREEN_BIAS, inGLState->colorBias[1]);
|
||||
glPixelTransferf(GL_BLUE_BIAS, inGLState->colorBias[2]);
|
||||
glPixelTransferf(GL_ALPHA_BIAS, inGLState->colorBias[3]);
|
||||
glPixelTransferf(GL_RED_SCALE, inGLState->colorScale[0]);
|
||||
glPixelTransferf(GL_GREEN_SCALE, inGLState->colorScale[1]);
|
||||
glPixelTransferf(GL_BLUE_SCALE, inGLState->colorScale[2]);
|
||||
glPixelTransferf(GL_ALPHA_SCALE, inGLState->colorScale[3]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((inAll || (inContext->enableState.glObjects
|
||||
&& (inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)))
|
||||
&& GLEW_ARB_vertex_buffer_object) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, inGLState->vertexBufferObjectID);
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_TRIANGLE))
|
||||
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
|
||||
inGLState->elementBufferObjectID);
|
||||
}
|
||||
|
||||
if (inAll || (inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
&& inContext->enableState.glObjects
|
||||
&& inContext->enableState.extrude))
|
||||
if (!inGLState->normalize)
|
||||
glDisable(GL_NORMALIZE);
|
||||
|
||||
if (inAll || inContext->renderState.renderStyle == GLC_LINE
|
||||
|| inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
|| (inContext->renderState.renderStyle == GLC_TEXTURE
|
||||
&& inContext->enableState.glObjects
|
||||
&& GLEW_ARB_vertex_buffer_object)) {
|
||||
if (!inGLState->vertexArray)
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(inGLState->vertexArraySize, inGLState->vertexArrayType,
|
||||
inGLState->vertexArrayStride,
|
||||
inGLState->vertexArrayPointer);
|
||||
if (!inGLState->normalArray)
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
if (!inGLState->colorArray)
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
if (!inGLState->indexArray)
|
||||
glDisableClientState(GL_INDEX_ARRAY);
|
||||
if (!inGLState->texCoordArray)
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
if (inAll || inContext->renderState.renderStyle == GLC_TEXTURE)
|
||||
glTexCoordPointer(inGLState->texCoordArraySize,
|
||||
inGLState->texCoordArrayType,
|
||||
inGLState->texCoordArrayStride,
|
||||
inGLState->texCoordArrayPointer);
|
||||
if (!inGLState->edgeFlagArray)
|
||||
glDisableClientState(GL_EDGE_FLAG_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef GLEW_MX
|
||||
/* Function for GLEW so that it can get a context */
|
||||
GLEWContext* __glcGetGlewContext(void)
|
||||
{
|
||||
__GLCcontext* ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &ctx->glewContext;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* This function initializes the thread management of QuesoGLC when TLS is not
|
||||
* available. It must be called once (see the macro GLC_INIT_THREAD)
|
||||
*/
|
||||
#ifndef HAVE_TLS
|
||||
void __glcInitThread(void) {
|
||||
#ifdef __WIN32__
|
||||
__glcCommonArea.threadID = GetCurrentThreadId();
|
||||
#else
|
||||
__glcCommonArea.threadID = pthread_self();
|
||||
#endif /* __WIN32__ */
|
||||
}
|
||||
#endif /* HAVE_TLS */
|
|
@ -0,0 +1,220 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCarray which is an array which size can grow as some
|
||||
* new elements are added to it.
|
||||
*/
|
||||
|
||||
/* This object heavily uses the realloc() which means that it must not be
|
||||
* assumed that the data are always stored at the same address. The safer way
|
||||
* to handle that is to *always* assume the address of the data has changed
|
||||
* *every* time a method of __GLCarray is called ; whatever the method is.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#define GLC_ARRAY_BLOCK_SIZE 16
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
* The user must give the size of an element of the array.
|
||||
*/
|
||||
__GLCarray* __glcArrayCreate(const int inElementSize)
|
||||
{
|
||||
__GLCarray* This = NULL;
|
||||
|
||||
This = (__GLCarray*)__glcMalloc(sizeof(__GLCarray));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCarray));
|
||||
|
||||
This->data = (char*)__glcMalloc(GLC_ARRAY_BLOCK_SIZE * inElementSize);
|
||||
if (!This->data) {
|
||||
__glcFree(This);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->allocated = GLC_ARRAY_BLOCK_SIZE;
|
||||
This->elementSize = inElementSize;
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcArrayDestroy(__GLCarray* This)
|
||||
{
|
||||
if (This->data) {
|
||||
assert(This->allocated);
|
||||
__glcFree(This->data);
|
||||
}
|
||||
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Allocate a new block of elements in the array 'This'. The function returns
|
||||
* NULL if it fails and raises an error accordingly. However the original
|
||||
* array is not lost and is kept untouched.
|
||||
*/
|
||||
static __GLCarray* __glcArrayUpdateSize(__GLCarray* This)
|
||||
{
|
||||
char* data = NULL;
|
||||
|
||||
data = (char*)__glcRealloc(This->data,
|
||||
(This->allocated + GLC_ARRAY_BLOCK_SIZE) * This->elementSize);
|
||||
if (!data) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
This->data = data;
|
||||
This->allocated += GLC_ARRAY_BLOCK_SIZE;
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Append a value to the array. The function may allocate some more room if
|
||||
* necessary
|
||||
*/
|
||||
__GLCarray* __glcArrayAppend(__GLCarray* This, const void* inValue)
|
||||
{
|
||||
/* Update the room if needed */
|
||||
if (This->length == This->allocated) {
|
||||
if (!__glcArrayUpdateSize(This))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Append the new element */
|
||||
memcpy(This->data + This->length*This->elementSize, inValue,
|
||||
This->elementSize);
|
||||
This->length++;
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Insert a value in the array at the rank inRank. The function may allocate
|
||||
* some more room if necessary
|
||||
*/
|
||||
__GLCarray* __glcArrayInsert(__GLCarray* This, const int inRank,
|
||||
const void* inValue)
|
||||
{
|
||||
/* Update the room if needed */
|
||||
if (This->length == This->allocated) {
|
||||
if (!__glcArrayUpdateSize(This))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Insert the new element */
|
||||
if (This->length > inRank)
|
||||
memmove(This->data + (inRank+1) * This->elementSize,
|
||||
This->data + inRank * This->elementSize,
|
||||
(This->length - inRank) * This->elementSize);
|
||||
|
||||
memcpy(This->data + inRank*This->elementSize, inValue, This->elementSize);
|
||||
This->length++;
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove an element from the array. For performance reasons, this function
|
||||
* does not release memory.
|
||||
*/
|
||||
void __glcArrayRemove(__GLCarray* This, const int inRank)
|
||||
{
|
||||
if (inRank < This->length-1)
|
||||
memmove(This->data + inRank * This->elementSize,
|
||||
This->data + (inRank+1) * This->elementSize,
|
||||
(This->length - inRank - 1) * This->elementSize);
|
||||
This->length--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Insert some room in the array at rank 'inRank' and leave it as is.
|
||||
* The difference between __glcArrayInsertCell() and __glcArrayInsert() is that
|
||||
* __glcArrayInsert() copy a value in the new element array while
|
||||
* __glcArrayInsertCell() does not. Moreover __glcArrayInsertCell() can insert
|
||||
* several cells in a row which is faster than calling __glcArrayInsert()
|
||||
* several times in a row.
|
||||
* This function is used to optimize performance in certain configurations.
|
||||
*/
|
||||
void* __glcArrayInsertCell(__GLCarray* This, const int inRank,
|
||||
const int inCells)
|
||||
{
|
||||
char* newCell = NULL;
|
||||
|
||||
assert(inCells < GLC_ARRAY_BLOCK_SIZE);
|
||||
|
||||
if ((This->length + inCells) > This->allocated) {
|
||||
if (!__glcArrayUpdateSize(This))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newCell = This->data + inRank * This->elementSize;
|
||||
|
||||
if (This->length > inRank)
|
||||
memmove(newCell + inCells * This->elementSize, newCell,
|
||||
(This->length - inRank) * This->elementSize);
|
||||
|
||||
This->length += inCells;
|
||||
|
||||
return (void*)newCell;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Duplicate an array */
|
||||
__GLCarray* __glcArrayDuplicate(__GLCarray* This)
|
||||
{
|
||||
__GLCarray* duplicate = NULL;
|
||||
|
||||
duplicate = (__GLCarray*)__glcMalloc(sizeof(__GLCarray));
|
||||
if (!duplicate) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(duplicate, This, sizeof(__GLCarray));
|
||||
|
||||
duplicate->data = (char*)__glcMalloc(This->allocated * This->elementSize);
|
||||
if (!duplicate->data) {
|
||||
__glcFree(duplicate);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(duplicate->data, This->data, This->allocated * This->elementSize);
|
||||
|
||||
return duplicate;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCarray which is an array which size can grow as
|
||||
* some new elements are added to it.
|
||||
*/
|
||||
|
||||
#ifndef __glc_oarray_h
|
||||
#define __glc_oarray_h
|
||||
|
||||
#define GLC_ARRAY_DATA(array) ((array)->data)
|
||||
#define GLC_ARRAY_LENGTH(array) ((array)->length)
|
||||
#define GLC_ARRAY_SIZE(array) (((array)->length) * ((array)->elementSize))
|
||||
|
||||
typedef struct __GLCarrayRec __GLCarray;
|
||||
|
||||
struct __GLCarrayRec {
|
||||
char* data;
|
||||
int allocated;
|
||||
int length;
|
||||
int elementSize;
|
||||
};
|
||||
|
||||
__GLCarray* __glcArrayCreate(int inElementSize);
|
||||
void __glcArrayDestroy(__GLCarray* This);
|
||||
__GLCarray* __glcArrayAppend(__GLCarray* This, const void* inValue);
|
||||
__GLCarray* __glcArrayInsert(__GLCarray* This, const int inRank,
|
||||
const void* inValue);
|
||||
void __glcArrayRemove(__GLCarray* This, const int inRank);
|
||||
void* __glcArrayInsertCell(__GLCarray* This, const int inRank,
|
||||
const int inCells);
|
||||
__GLCarray* __glcArrayDuplicate(__GLCarray* This);
|
||||
#endif
|
|
@ -0,0 +1,599 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCcharMap which manage the charmaps of both the fonts
|
||||
* and the masters. One of the purpose of this object is to encapsulate the
|
||||
* FcCharSet structure from Fontconfig and to add it some more functionalities.
|
||||
* It also allows to centralize the character map management for easier
|
||||
* maintenance.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
* The user must give the FcPattern of the font or the master (which may be NULL
|
||||
* in which case the character map will be empty).
|
||||
*/
|
||||
__GLCcharMap* __glcCharMapCreate(const __GLCmaster* inMaster,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
__GLCcharMap* This = NULL;
|
||||
|
||||
assert(inContext);
|
||||
|
||||
This = (__GLCcharMap*)__glcMalloc(sizeof(__GLCcharMap));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCcharMap));
|
||||
|
||||
This->charSet = FcCharSetCreate();
|
||||
if (!This->charSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (inMaster) {
|
||||
FcCharSet* charSet = NULL;
|
||||
FcFontSet* fontSet = NULL;
|
||||
int i = 0;
|
||||
FcObjectSet* objectSet = NULL;
|
||||
FcPattern* pattern = FcPatternCreate();
|
||||
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcCharSetDestroy(This->charSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
objectSet = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, FC_SPACING, FC_OUTLINE,
|
||||
FC_CHARSET, NULL);
|
||||
if (!objectSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcPatternDestroy(pattern);
|
||||
FcCharSetDestroy(This->charSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fontSet = FcFontList(inContext->config, pattern, objectSet);
|
||||
FcObjectSetDestroy(objectSet);
|
||||
FcPatternDestroy(pattern);
|
||||
if (!fontSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcCharSetDestroy(This->charSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < fontSet->nfont; i++) {
|
||||
FcChar8* family = NULL;
|
||||
int fixed = 0;
|
||||
FcChar8* foundry = NULL;
|
||||
FcBool outline = FcFalse;
|
||||
FcBool equal = FcFalse;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcResultMatch;
|
||||
|
||||
result = FcPatternGetBool(fontSet->fonts[i], FC_OUTLINE, 0, &outline);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetBool(fontSet->fonts[i], FC_OUTLINE, 0, &outline);
|
||||
#endif
|
||||
|
||||
/* Check whether the glyphs are outlines */
|
||||
if (!outline)
|
||||
continue;
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &family);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetString(fontSet->fonts[i], FC_FOUNDRY, 0, &foundry);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetInteger(fontSet->fonts[i], FC_SPACING, 0, &fixed);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &family);
|
||||
FcPatternGetString(fontSet->fonts[i], FC_FOUNDRY, 0, &foundry);
|
||||
FcPatternGetInteger(fontSet->fonts[i], FC_SPACING, 0, &fixed);
|
||||
#endif
|
||||
|
||||
if (foundry)
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
|
||||
FC_FOUNDRY, FcTypeString, foundry, FC_SPACING,
|
||||
FcTypeInteger, fixed, NULL);
|
||||
else
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
|
||||
FC_SPACING, FcTypeInteger, fixed, NULL);
|
||||
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcCharSetDestroy(This->charSet);
|
||||
FcFontSetDestroy(fontSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
equal = FcPatternEqual(pattern, inMaster->pattern);
|
||||
FcPatternDestroy(pattern);
|
||||
if (equal) {
|
||||
FcCharSet* newCharSet = NULL;
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetCharSet(fontSet->fonts[i], FC_CHARSET, 0,
|
||||
&charSet);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetCharSet(fontSet->fonts[i], FC_CHARSET, 0, &charSet);
|
||||
#endif
|
||||
|
||||
newCharSet = FcCharSetUnion(This->charSet, charSet);
|
||||
if (!newCharSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcCharSetDestroy(This->charSet);
|
||||
FcFontSetDestroy(fontSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FcCharSetDestroy(This->charSet);
|
||||
This->charSet = newCharSet;
|
||||
}
|
||||
}
|
||||
|
||||
FcFontSetDestroy(fontSet);
|
||||
}
|
||||
|
||||
/* The array 'map' will contain the actual character map */
|
||||
This->map = __glcArrayCreate(sizeof(__GLCcharMapElement));
|
||||
if (!This->map) {
|
||||
FcCharSetDestroy(This->charSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcCharMapDestroy(__GLCcharMap* This)
|
||||
{
|
||||
if (This->map)
|
||||
__glcArrayDestroy(This->map);
|
||||
|
||||
FcCharSetDestroy(This->charSet);
|
||||
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Add a given character to the character map. Afterwards, the character map
|
||||
* will associate the glyph 'inGlyph' to the Unicode codepoint 'inCode'.
|
||||
*/
|
||||
void __glcCharMapAddChar(__GLCcharMap* This, const GLint inCode,
|
||||
__GLCglyph* inGlyph)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
__GLCcharMapElement* newElement = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to look for the place where to add the new
|
||||
* character.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
/* If the character map already contains the new character then update the
|
||||
* glyph then return.
|
||||
*/
|
||||
if (element[middle].mappedCode == (GLCulong)inCode) {
|
||||
element[middle].glyph = inGlyph;
|
||||
return;
|
||||
}
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* If we have reached the end of the array then update the rank 'middle'
|
||||
* accordingly.
|
||||
*/
|
||||
if ((end >= 0) && (element[middle].mappedCode < (GLCulong)inCode))
|
||||
middle++;
|
||||
|
||||
/* Insert the new character in the character map */
|
||||
newElement = (__GLCcharMapElement*)__glcArrayInsertCell(This->map, middle, 1);
|
||||
if (!newElement)
|
||||
return;
|
||||
|
||||
newElement->mappedCode = inCode;
|
||||
newElement->glyph = inGlyph;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove a character from the character map */
|
||||
void __glcCharMapRemoveChar(__GLCcharMap* This, const GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to look for the place where to add the new
|
||||
* character.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
/* When the character is found remove it from the array and return */
|
||||
if (element[middle].mappedCode == (GLCulong)inCode) {
|
||||
__glcArrayRemove(This->map, middle);
|
||||
break;
|
||||
}
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the Unicode character name of the character which codepoint is inCode.
|
||||
* Note : since the character maps of the fonts can be altered, this function
|
||||
* can return 'LATIN CAPITAL LETTER B' whereas inCode contained 65 (which is
|
||||
* the Unicode code point of 'LATIN CAPITAL LETTER A').
|
||||
*/
|
||||
const GLCchar8* __glcCharMapGetCharName(const __GLCcharMap* This,
|
||||
const GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
GLint code = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to look for the Unicode codepoint that the
|
||||
* request character maps to.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
if (element[middle].mappedCode == (GLCulong)inCode) {
|
||||
code = element[middle].glyph->codepoint;
|
||||
break;
|
||||
}
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
if (FcCharSetHasChar(This->charSet, inCode))
|
||||
code = inCode;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return __glcNameFromCode(code);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the glyph corresponding to codepoint 'inCode' */
|
||||
__GLCglyph* __glcCharMapGetGlyph(const __GLCcharMap* This, const GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to find the glyph of the requested
|
||||
* character.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
if (element[middle].mappedCode == (GLCulong)inCode)
|
||||
/* When the character is found return the corresponding glyph */
|
||||
return element[middle].glyph;
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* No glyph has been defined yet for the requested character */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Check if a character is in the character map */
|
||||
GLboolean __glcCharMapHasChar(const __GLCcharMap* This, const GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to find the requested character. */
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
/* The character has been found : return GL_TRUE */
|
||||
if (element[middle].mappedCode == (GLCulong)inCode)
|
||||
return GL_TRUE;
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* Check if the character identified by inCode exists in the font */
|
||||
return FcCharSetHasChar(This->charSet, inCode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function counts the number of bits that are set in c1
|
||||
* Copied from Keith Packard's fontconfig
|
||||
*/
|
||||
static GLCchar32 __glcCharSetPopCount(const GLCchar32 c1)
|
||||
{
|
||||
/* hackmem 169 */
|
||||
GLCchar32 c2 = (c1 >> 1) & 033333333333;
|
||||
c2 = c1 - c2 - ((c2 >> 1) & 033333333333);
|
||||
return (((c2 + (c2 >> 3)) & 030707070707) % 077);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the name of the character which is stored at rank 'inIndex' in the
|
||||
* FcCharSet of the face.
|
||||
*/
|
||||
const GLCchar8* __glcCharMapGetCharNameByIndex(const __GLCcharMap* This,
|
||||
const GLint inIndex)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
|
||||
/* In Fontconfig the map in FcCharSet is organized as an array of integers.
|
||||
* Each integer corresponds to a page of 32 characters (since it uses 32 bits
|
||||
* integer). If a bit is set then the corresponding character is in the
|
||||
* character map otherwise it is not.
|
||||
* In order not to store pages of 0's, the character map begins at the
|
||||
* character which codepoint is 'base'.
|
||||
* Pages are also gathered in blocks of 'FC_CHARSET_MAP_SIZE' pages in order
|
||||
* to prevent Fontconfig to store heaps of 0's if the character codes are
|
||||
* sparsed.
|
||||
*
|
||||
* The codepoint of a character located at bit 'j' of page 'i' is :
|
||||
* 'base + (i << 5) + j'.
|
||||
*/
|
||||
GLCchar32 map[FC_CHARSET_MAP_SIZE];
|
||||
GLCchar32 next = 0;
|
||||
GLCchar32 base = 0;
|
||||
GLCchar32 count = 1;
|
||||
GLCchar32 value = 0;
|
||||
|
||||
assert(This);
|
||||
assert(inIndex >= 0);
|
||||
|
||||
base = FcCharSetFirstPage(This->charSet, map, &next);
|
||||
|
||||
do {
|
||||
/* Parse the pages in FcCharSet */
|
||||
for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) {
|
||||
/* Get the number of character located in the current page */
|
||||
value = __glcCharSetPopCount(map[i]);
|
||||
|
||||
/* Check if the character we are looking for is in the current page */
|
||||
if (count + value >= (GLCchar32)inIndex) {
|
||||
for (j = 0; j < 32; j++) {
|
||||
/* Parse the page bit by bit */
|
||||
if ((map[i] >> j) & 1) {
|
||||
count++; /* A character is set at bit j */
|
||||
/* Check if we have reached the rank inIndex */
|
||||
if (count == (GLCchar32)inIndex) {
|
||||
/* Get the character name */
|
||||
return __glcNameFromCode(base + (i << 5) + j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Add the number of characters of the current page to the count and
|
||||
* check the next page.
|
||||
*/
|
||||
count += value;
|
||||
}
|
||||
/* The current block is finished, check the next one */
|
||||
base = FcCharSetNextPage(This->charSet, map, &next);
|
||||
} while (base != FC_CHARSET_DONE);
|
||||
|
||||
/* The character has not been found */
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the maximum mapped code of a character set */
|
||||
GLint __glcCharMapGetMaxMappedCode(const __GLCcharMap* This)
|
||||
{
|
||||
GLCchar32 base = 0;
|
||||
GLCchar32 next = 0;
|
||||
GLCchar32 prev_base = 0;
|
||||
GLCchar32 map[FC_CHARSET_MAP_SIZE];
|
||||
int i = 0, j = 0;
|
||||
GLCulong maxMappedCode = 0;
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int length = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
|
||||
/* Look for the last block of pages of the FcCharSet structure */
|
||||
base = FcCharSetFirstPage(This->charSet, map, &next);
|
||||
assert(base != FC_CHARSET_DONE);
|
||||
|
||||
do {
|
||||
prev_base = base;
|
||||
base = FcCharSetNextPage(This->charSet, map, &next);
|
||||
} while (base != FC_CHARSET_DONE);
|
||||
|
||||
/* Parse the pages in descending order to find the last page that contains
|
||||
* one character.
|
||||
*/
|
||||
for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
|
||||
if (map[i]) break;
|
||||
|
||||
/* If the map contains no char then something went wrong... */
|
||||
assert(i >= 0);
|
||||
|
||||
/* Parse the bits of the last page in descending order to find the last
|
||||
* character of the page
|
||||
*/
|
||||
for (j = 31; j >= 0; j--)
|
||||
if ((map[i] >> j) & 1) break;
|
||||
|
||||
assert(j >= 0);
|
||||
|
||||
/* Calculate the max mapped code */
|
||||
maxMappedCode = prev_base + (i << 5) + j;
|
||||
|
||||
/* Check that a code greater than the one found in the FcCharSet is not
|
||||
* stored in the array 'map'.
|
||||
*/
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
length = GLC_ARRAY_LENGTH(This->map);
|
||||
|
||||
/* Return the greater of the code of both the FcCharSet and the array 'map'*/
|
||||
if (length)
|
||||
return element[length-1].mappedCode > maxMappedCode ?
|
||||
element[length-1].mappedCode : maxMappedCode;
|
||||
else
|
||||
return maxMappedCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the minimum mapped code of a character set */
|
||||
GLint __glcCharMapGetMinMappedCode(const __GLCcharMap* This)
|
||||
{
|
||||
GLCchar32 base = 0;
|
||||
GLCchar32 next = 0;
|
||||
GLCchar32 map[FC_CHARSET_MAP_SIZE];
|
||||
int i = 0, j = 0;
|
||||
GLCulong minMappedCode = 0xffffffff;
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int length = 0;
|
||||
|
||||
assert(This);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
|
||||
/* Get the first block of pages of the FcCharSet structure */
|
||||
base = FcCharSetFirstPage(This->charSet, map, &next);
|
||||
assert(base != FC_CHARSET_DONE);
|
||||
|
||||
/* Parse the pages in ascending order to find the first page that contains
|
||||
* one character.
|
||||
*/
|
||||
for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
|
||||
if (map[i]) break;
|
||||
|
||||
/* If the map contains no char then something went wrong... */
|
||||
assert(i >= 0);
|
||||
|
||||
/* Parse the bits of the first page in ascending order to find the first
|
||||
* character of the page
|
||||
*/
|
||||
for (j = 0; j < 32; j++)
|
||||
if ((map[i] >> j) & 1) break;
|
||||
|
||||
assert(j < 32);
|
||||
minMappedCode = base + (i << 5) + j;
|
||||
|
||||
/* Check that a code lower than the one found in the FcCharSet is not
|
||||
* stored in the array 'map'.
|
||||
*/
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
length = GLC_ARRAY_LENGTH(This->map);
|
||||
|
||||
/* Return the lower of the code of both the FcCharSet and the array 'map'*/
|
||||
if (length)
|
||||
return element[0].mappedCode < minMappedCode ?
|
||||
element[0].mappedCode : minMappedCode;
|
||||
else
|
||||
return minMappedCode;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCcharMap which manage the charmaps of both the fonts
|
||||
* and the masters.
|
||||
*/
|
||||
|
||||
#ifndef __glc_ocharmap_h
|
||||
#define __glc_ocharmap_h
|
||||
|
||||
#include "ocontext.h"
|
||||
#include "oglyph.h"
|
||||
|
||||
typedef struct __GLCcharMapElementRec __GLCcharMapElement;
|
||||
typedef struct __GLCcharMapRec __GLCcharMap;
|
||||
typedef struct __GLCmasterRec __GLCmaster;
|
||||
|
||||
struct __GLCcharMapElementRec {
|
||||
GLCulong mappedCode;
|
||||
__GLCglyph* glyph;
|
||||
};
|
||||
|
||||
struct __GLCcharMapRec {
|
||||
FcCharSet* charSet;
|
||||
__GLCarray* map;
|
||||
};
|
||||
|
||||
__GLCcharMap* __glcCharMapCreate(const __GLCmaster* inMaster,
|
||||
const __GLCcontext* inContext);
|
||||
void __glcCharMapDestroy(__GLCcharMap* This);
|
||||
void __glcCharMapAddChar(__GLCcharMap* This, const GLint inCode,
|
||||
__GLCglyph* inGlyph);
|
||||
void __glcCharMapRemoveChar(__GLCcharMap* This, const GLint inCode);
|
||||
const GLCchar8* __glcCharMapGetCharName(const __GLCcharMap* This,
|
||||
const GLint inCode);
|
||||
__GLCglyph* __glcCharMapGetGlyph(const __GLCcharMap* This, const GLint inCode);
|
||||
GLboolean __glcCharMapHasChar(const __GLCcharMap* This, const GLint inCode);
|
||||
const GLCchar8* __glcCharMapGetCharNameByIndex(const __GLCcharMap* This,
|
||||
const GLint inIndex);
|
||||
/* Return the number of characters in the character map */
|
||||
static inline GLint __glcCharMapGetCount(const __GLCcharMap* This)
|
||||
{
|
||||
assert(This);
|
||||
assert(This->charSet);
|
||||
return FcCharSetCount(This->charSet);
|
||||
}
|
||||
GLint __glcCharMapGetMaxMappedCode(const __GLCcharMap* This);
|
||||
GLint __glcCharMapGetMinMappedCode(const __GLCcharMap* This);
|
||||
#endif
|
|
@ -0,0 +1,917 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCcontext which is used to manage the contexts.
|
||||
*/
|
||||
|
||||
#if defined(__WIN32__) || defined(_MSC_VER)
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "texture.h"
|
||||
#include FT_MODULE_H
|
||||
|
||||
__GLCcommonArea __glcCommonArea;
|
||||
#ifdef HAVE_TLS
|
||||
__thread __GLCthreadArea __glcTlsThreadArea;
|
||||
#else
|
||||
__GLCthreadArea* __glcThreadArea = NULL;
|
||||
#endif
|
||||
|
||||
static GLboolean __glcContextUpdateHashTable(__GLCcontext *This);
|
||||
|
||||
|
||||
|
||||
/* Find a token in a list of tokens separated by 'separator' */
|
||||
static GLCchar8* __glcFindIndexList(GLCchar8* inString, const GLuint inIndex,
|
||||
const GLCchar8* inSeparator)
|
||||
{
|
||||
GLuint occurence = 0;
|
||||
GLCchar8* s = inString;
|
||||
const GLCchar8* sep = inSeparator;
|
||||
|
||||
if (!inIndex)
|
||||
return s;
|
||||
|
||||
for (; *s != '\0'; s++) {
|
||||
if (*s == *sep) {
|
||||
occurence++;
|
||||
if (occurence == inIndex)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (GLCchar8 *) s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
*/
|
||||
__GLCcontext* __glcContextCreate(const GLint inContext)
|
||||
{
|
||||
__GLCcontext *This = NULL;
|
||||
|
||||
This = (__GLCcontext*)__glcMalloc(sizeof(__GLCcontext));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCcontext));
|
||||
|
||||
if (FT_New_Library(&__glcCommonArea.memoryManager, &This->library)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FT_Add_Default_Modules(This->library);
|
||||
|
||||
#ifdef GLC_FT_CACHE
|
||||
if (FTC_Manager_New(This->library, 0, 0, 0, __glcFileOpen, NULL,
|
||||
&This->cache)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
__glcLock();
|
||||
This->config = FcInitLoadConfigAndFonts();
|
||||
__glcUnlock();
|
||||
if (!This->config) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->catalogList = __glcArrayCreate(sizeof(GLCchar8*));
|
||||
if (!This->catalogList) {
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->masterHashTable = __glcArrayCreate(sizeof(GLCchar32));
|
||||
if (!This->masterHashTable) {
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!__glcContextUpdateHashTable(This)) {
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->currentFontList.head = NULL;
|
||||
This->currentFontList.tail = NULL;
|
||||
This->fontList.head = NULL;
|
||||
This->fontList.tail = NULL;
|
||||
This->genFontList.head = NULL;
|
||||
This->genFontList.tail = NULL;
|
||||
|
||||
This->isCurrent = GL_FALSE;
|
||||
This->isInGlobalCommand = GL_FALSE;
|
||||
This->id = inContext;
|
||||
This->pendingDelete = GL_FALSE;
|
||||
This->stringState.callback = (GLCfunc)GLC_NONE;
|
||||
This->stringState.dataPointer = (void*)GLC_NONE;
|
||||
This->stringState.stringType = GLC_UCS1;
|
||||
This->enableState.autoFont = GL_TRUE;
|
||||
This->enableState.glObjects = GL_TRUE;
|
||||
This->enableState.mipmap = GL_TRUE;
|
||||
This->enableState.hinting = GL_FALSE;
|
||||
This->enableState.extrude = GL_FALSE;
|
||||
This->enableState.kerning = GL_FALSE;
|
||||
This->renderState.resolution = 72.;
|
||||
This->renderState.renderStyle = GLC_BITMAP;
|
||||
This->renderState.tolerance = 0.005;
|
||||
This->bitmapMatrixStackDepth = 1;
|
||||
This->bitmapMatrix = This->bitmapMatrixStack;
|
||||
This->bitmapMatrix[0] = 1.;
|
||||
This->bitmapMatrix[3] = 1.;
|
||||
This->measurementBuffer = __glcArrayCreate(12 * sizeof(GLfloat));
|
||||
if (!This->measurementBuffer) {
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
This->isInCallbackFunc = GL_FALSE;
|
||||
This->vertexArray = __glcArrayCreate(2 * sizeof(GLfloat));
|
||||
if (!This->vertexArray) {
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
This->controlPoints = __glcArrayCreate(5 * sizeof(GLfloat));
|
||||
if (!This->controlPoints) {
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
This->endContour = __glcArrayCreate(sizeof(int));
|
||||
if (!This->endContour) {
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->vertexIndices = __glcArrayCreate(sizeof(GLuint));
|
||||
if (!This->vertexIndices) {
|
||||
__glcArrayDestroy(This->endContour);
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->geomBatches = __glcArrayCreate(sizeof(__GLCgeomBatch));
|
||||
if (!This->geomBatches) {
|
||||
__glcArrayDestroy(This->vertexIndices);
|
||||
__glcArrayDestroy(This->endContour);
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The environment variable GLC_PATH is an alternate way to allow QuesoGLC
|
||||
* to access to fonts catalogs/directories.
|
||||
*/
|
||||
/*Check if the GLC_PATH environment variables are exported */
|
||||
if (getenv("GLC_CATALOG_LIST") || getenv("GLC_PATH")) {
|
||||
GLCchar8 *path = NULL;
|
||||
GLCchar8 *begin = NULL;
|
||||
GLCchar8 *sepPos = NULL;
|
||||
const GLCchar8 *separator = (GLCchar8*)getenv("GLC_LIST_SEPARATOR");
|
||||
|
||||
/* Get the list separator */
|
||||
if (!separator) {
|
||||
#ifdef __WIN32__
|
||||
/* Windows can not use a colon-separated list since the colon sign is
|
||||
* used after the drive letter. The semicolon is used by Windows for its
|
||||
* PATH variable, so we use it for consistency.
|
||||
*/
|
||||
separator = (const GLCchar8*)";";
|
||||
#else
|
||||
/* POSIX platforms use colon-separated lists for the paths variables
|
||||
* so we keep with it for consistency.
|
||||
*/
|
||||
separator = (const GLCchar8*)":";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Read the paths of fonts file.
|
||||
* First, try GLC_CATALOG_LIST...
|
||||
*/
|
||||
if (getenv("GLC_CATALOG_LIST"))
|
||||
#ifdef __WIN32__
|
||||
path = (GLCchar8*)_strdup(getenv("GLC_CATALOG_LIST"));
|
||||
#else
|
||||
path = (GLCchar8*)strdup(getenv("GLC_CATALOG_LIST"));
|
||||
#endif
|
||||
/* Then try GLC_PATH */
|
||||
else if (getenv("GLC_PATH")) {
|
||||
#ifdef __WIN32__
|
||||
path = (GLCchar8*)_strdup(getenv("GLC_PATH"));
|
||||
#else
|
||||
path = (GLCchar8*)strdup(getenv("GLC_PATH"));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (path) {
|
||||
/* Get each path and add the corresponding masters to the current
|
||||
* context */
|
||||
GLCchar8* duplicated = NULL;
|
||||
|
||||
begin = path;
|
||||
do {
|
||||
sepPos = __glcFindIndexList(begin, 1, separator);
|
||||
|
||||
if (*sepPos)
|
||||
*(sepPos++) = 0;
|
||||
|
||||
#ifdef __WIN32__
|
||||
duplicated = (GLCchar8*)_strdup((char*)begin);
|
||||
#else
|
||||
duplicated = (GLCchar8*)strdup((char*)begin);
|
||||
#endif
|
||||
if (!duplicated) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
else {
|
||||
if (!__glcArrayAppend(This->catalogList, &duplicated))
|
||||
free(duplicated);
|
||||
else if (!FcConfigAppFontAddDir(This->config,
|
||||
(const unsigned char*)begin)) {
|
||||
__glcArrayRemove(This->catalogList,
|
||||
GLC_ARRAY_LENGTH(This->catalogList));
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
free(duplicated);
|
||||
}
|
||||
else if (!__glcContextUpdateHashTable(This)) {
|
||||
/* For some reason the update of the master hash table has failed :
|
||||
* the new catalog must then be removed from GLC_CATALOG_LIST.
|
||||
*/
|
||||
__glcContextRemoveCatalog(This,
|
||||
GLC_ARRAY_LENGTH(This->catalogList));
|
||||
}
|
||||
}
|
||||
|
||||
begin = sepPos;
|
||||
} while (*sepPos);
|
||||
free(path);
|
||||
}
|
||||
else {
|
||||
/* strdup has failed to allocate memory to duplicate GLC_PATH => ERROR */
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef GLC_FT_CACHE
|
||||
/* This function is called from FT_List_Finalize() to close all the fonts
|
||||
* of the GLC_CURRENT_FONT_LIST
|
||||
*/
|
||||
static void __glcFontClosure(FT_Memory GLC_UNUSED_ARG(inMemory), void* inData,
|
||||
void* GLC_UNUSED_ARG(inUser))
|
||||
{
|
||||
__GLCfont *font = (__GLCfont*)inData;
|
||||
|
||||
assert(font);
|
||||
__glcFontClose(font);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* This function is called from FT_List_Finalize() to destroy all
|
||||
* remaining fonts
|
||||
*/
|
||||
static void __glcFontDestructor(FT_Memory GLC_UNUSED_ARG(inMemory),
|
||||
void* inData, void* inUser)
|
||||
{
|
||||
__GLCfont *font = (__GLCfont*)inData;
|
||||
__GLCcontext* ctx = (__GLCcontext*)inUser;
|
||||
|
||||
assert(ctx);
|
||||
assert(font);
|
||||
__glcFontDestroy(font, ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object : it first destroys all the GLC objects that have
|
||||
* been created during the life of the context. Then it releases the memory
|
||||
* occupied by the GLC state struct.
|
||||
* It does not destroy GL objects associated with the GLC context since we can
|
||||
* not be sure that the current GL context is the GL context that contains the
|
||||
* GL objects built by the GLC context that we are destroying. This could
|
||||
* happen if the user calls glcDeleteContext() after the GL context has been
|
||||
* destroyed or after the user has changed the current GL context.
|
||||
*/
|
||||
void __glcContextDestroy(__GLCcontext *This)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
assert(This);
|
||||
|
||||
/* Destroy the list of catalogs */
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(This->catalogList); i++) {
|
||||
GLCchar8* string = ((GLCchar8**)GLC_ARRAY_DATA(This->catalogList))[i];
|
||||
|
||||
assert(string);
|
||||
free(string);
|
||||
}
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
|
||||
/* Destroy GLC_CURRENT_FONT_LIST */
|
||||
#ifdef GLC_FT_CACHE
|
||||
FT_List_Finalize(&This->currentFontList, NULL,
|
||||
&__glcCommonArea.memoryManager, NULL);
|
||||
#else
|
||||
FT_List_Finalize(&This->currentFontList, __glcFontClosure,
|
||||
&__glcCommonArea.memoryManager, NULL);
|
||||
#endif
|
||||
|
||||
/* Destroy GLC_FONT_LIST */
|
||||
FT_List_Finalize(&This->fontList, __glcFontDestructor,
|
||||
&__glcCommonArea.memoryManager, This);
|
||||
/* Destroy empty fonts generated by glcGenFontID() */
|
||||
FT_List_Finalize(&This->genFontList, __glcFontDestructor,
|
||||
&__glcCommonArea.memoryManager, This);
|
||||
|
||||
if (This->masterHashTable)
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
|
||||
FT_List_Finalize(&This->atlasList, NULL,
|
||||
&__glcCommonArea.memoryManager, NULL);
|
||||
|
||||
if (This->bufferSize)
|
||||
__glcFree(This->buffer);
|
||||
|
||||
if (This->measurementBuffer)
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
|
||||
if (This->vertexArray)
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
|
||||
if (This->controlPoints)
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
|
||||
if (This->endContour)
|
||||
__glcArrayDestroy(This->endContour);
|
||||
|
||||
if (This->vertexIndices)
|
||||
__glcArrayDestroy(This->vertexIndices);
|
||||
|
||||
if (This->geomBatches)
|
||||
__glcArrayDestroy(This->geomBatches);
|
||||
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
FcConfigDestroy(This->config);
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the first font in GLC_CURRENT_FONT_LIST that maps 'inCode'.
|
||||
* If there is no such font, the function returns NULL.
|
||||
* 'inCode' must be given in UCS-4 format.
|
||||
*/
|
||||
static __GLCfont* __glcLookupFont(const FT_List fontList, const GLint inCode)
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
for (node = fontList->head; node; node = node->next) {
|
||||
__GLCfont* font = (__GLCfont*)node->data;
|
||||
|
||||
/* Check if the character identified by inCode exists in the font */
|
||||
if (__glcFontHasChar(font, inCode))
|
||||
return font;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Calls the callback function (does various tests to determine if it is
|
||||
* possible) and returns GL_TRUE if it has succeeded or GL_FALSE otherwise.
|
||||
* 'inCode' must be given in UCS-4 format.
|
||||
*/
|
||||
static GLboolean __glcCallCallbackFunc(__GLCcontext *inContext,
|
||||
const GLint inCode)
|
||||
{
|
||||
GLCfunc callbackFunc = NULL;
|
||||
GLboolean result = GL_FALSE;
|
||||
GLint aCode = 0;
|
||||
|
||||
/* Recursivity is not allowed */
|
||||
if (inContext->isInCallbackFunc)
|
||||
return GL_FALSE;
|
||||
|
||||
callbackFunc = inContext->stringState.callback;
|
||||
if (!callbackFunc)
|
||||
return GL_FALSE;
|
||||
|
||||
/* Convert the character code back to the current string type */
|
||||
aCode = __glcConvertUcs4ToGLint(inContext, inCode);
|
||||
/* Check if the character has been converted */
|
||||
if (aCode < 0)
|
||||
return GL_FALSE;
|
||||
|
||||
inContext->isInCallbackFunc = GL_TRUE;
|
||||
/* Call the callback function with the character converted to the current
|
||||
* string type.
|
||||
*/
|
||||
result = (*callbackFunc)(aCode);
|
||||
inContext->isInCallbackFunc = GL_FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Returns the ID of the first font in GLC_CURRENT_FONT_LIST that maps
|
||||
* 'inCode'. If there is no such font and GLC_AUTO_FONT is enabled, the
|
||||
* function attempts to append a new font from GLC_FONT_LIST (or from a master)
|
||||
* to GLC_CURRENT_FONT_LIST. If the attempt fails the function returns zero.
|
||||
* 'inCode' must be given in UCS-4 format.
|
||||
*/
|
||||
__GLCfont* __glcContextGetFont(__GLCcontext *This, const GLint inCode)
|
||||
{
|
||||
__GLCfont* font = NULL;
|
||||
|
||||
/* Look for a font in the current font list */
|
||||
font = __glcLookupFont(&This->currentFontList, inCode);
|
||||
/* If a font has been found return */
|
||||
if (font)
|
||||
return font;
|
||||
|
||||
/* If a callback function is defined for GLC_OP_glcUnmappedCode then call it.
|
||||
* The callback function should return GL_TRUE if it succeeds in appending to
|
||||
* GLC_CURRENT_FONT_LIST the ID of a font that maps 'inCode'.
|
||||
*/
|
||||
if (__glcCallCallbackFunc(This, inCode)) {
|
||||
font = __glcLookupFont(&This->currentFontList, inCode);
|
||||
if (font)
|
||||
return font;
|
||||
}
|
||||
|
||||
/* If the value of the boolean variable GLC_AUTOFONT is GL_TRUE then search
|
||||
* GLC_FONT_LIST for the first font that maps 'inCode'. If the search
|
||||
* succeeds, then append the font's ID to GLC_CURRENT_FONT_LIST.
|
||||
*/
|
||||
if (This->enableState.autoFont) {
|
||||
__GLCmaster* master = NULL;
|
||||
|
||||
font = __glcLookupFont(&This->fontList, inCode);
|
||||
if (font) {
|
||||
__glcAppendFont(This, font);
|
||||
return font;
|
||||
}
|
||||
|
||||
master = __glcMasterMatchCode(This, inCode);
|
||||
if (!master)
|
||||
return NULL;
|
||||
font = __glcNewFontFromMaster(__glcGenFontID(This), master, This, inCode);
|
||||
__glcMasterDestroy(master);
|
||||
|
||||
if (font) {
|
||||
/* Add the font to the GLC_CURRENT_FONT_LIST */
|
||||
__glcAppendFont(This, font);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Sometimes informations may need to be stored temporarily by a thread.
|
||||
* The so-called 'buffer' is created for that purpose. Notice that it is a
|
||||
* component of the GLC state struct hence its lifetime is the same as the
|
||||
* GLC state's lifetime.
|
||||
* __glcContextQueryBuffer() should be called whenever the buffer is to be used
|
||||
* The function checks that the buffer is big enough to store the required data
|
||||
* and returns a pointer to the buffer.
|
||||
* Note that the only memory management function used below is 'realloc' which
|
||||
* means that the buffer goes bigger and bigger until it is freed. No function
|
||||
* is provided to reduce its size so it should be freed and re-allocated
|
||||
* manually in case of emergency ;-)
|
||||
*/
|
||||
GLCchar* __glcContextQueryBuffer(__GLCcontext *This, const size_t inSize)
|
||||
{
|
||||
GLCchar* buffer = This->buffer;
|
||||
|
||||
if (inSize > This->bufferSize) {
|
||||
buffer = (GLCchar*)__glcRealloc(This->buffer, inSize);
|
||||
if (!buffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
else {
|
||||
This->buffer = buffer;
|
||||
This->bufferSize = inSize;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Update the hash table that which is used to convert master IDs into
|
||||
* FontConfig patterns.
|
||||
*/
|
||||
static GLboolean __glcContextUpdateHashTable(__GLCcontext *This)
|
||||
{
|
||||
FcPattern* pattern = NULL;
|
||||
FcObjectSet* objectSet = NULL;
|
||||
FcFontSet *fontSet = NULL;
|
||||
int i = 0;
|
||||
__GLCarray *updatedHashTable = NULL;
|
||||
|
||||
/* Use Fontconfig to get the default font files */
|
||||
pattern = FcPatternCreate();
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
objectSet = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, FC_OUTLINE, FC_SPACING,
|
||||
NULL);
|
||||
if (!objectSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcPatternDestroy(pattern);
|
||||
return GL_FALSE;
|
||||
}
|
||||
fontSet = FcFontList(This->config, pattern, objectSet);
|
||||
FcPatternDestroy(pattern);
|
||||
FcObjectSetDestroy(objectSet);
|
||||
if (!fontSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
updatedHashTable = __glcArrayDuplicate(This->masterHashTable);
|
||||
if (!updatedHashTable) {
|
||||
FcFontSetDestroy(fontSet);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
/* Parse the font set looking for fonts that are not already registered in the
|
||||
* hash table.
|
||||
*/
|
||||
for (i = 0; i < fontSet->nfont; i++) {
|
||||
GLCchar32 hashValue = 0;
|
||||
int j = 0;
|
||||
const int length = GLC_ARRAY_LENGTH(updatedHashTable);
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(updatedHashTable);
|
||||
FcBool outline = FcFalse;
|
||||
FcChar8* family = NULL;
|
||||
int fixed = 0;
|
||||
FcChar8* foundry = NULL;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcResultMatch;
|
||||
|
||||
result = FcPatternGetBool(fontSet->fonts[i], FC_OUTLINE, 0, &outline);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetBool(fontSet->fonts[i], FC_OUTLINE, 0, &outline);
|
||||
#endif
|
||||
|
||||
/* Check whether the glyphs are outlines */
|
||||
if (!outline)
|
||||
continue;
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &family);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetString(fontSet->fonts[i], FC_FOUNDRY, 0, &foundry);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetInteger(fontSet->fonts[i], FC_SPACING, 0, &fixed);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &family);
|
||||
FcPatternGetString(fontSet->fonts[i], FC_FOUNDRY, 0, &foundry);
|
||||
FcPatternGetInteger(fontSet->fonts[i], FC_SPACING, 0, &fixed);
|
||||
#endif
|
||||
|
||||
if (foundry)
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
|
||||
FC_FOUNDRY, FcTypeString, foundry, FC_SPACING,
|
||||
FcTypeInteger, fixed, NULL);
|
||||
else
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
|
||||
FC_SPACING, FcTypeInteger, fixed, NULL);
|
||||
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
__glcArrayDestroy(updatedHashTable);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
/* Check if the font is already registered in the hash table */
|
||||
hashValue = FcPatternHash(pattern);
|
||||
FcPatternDestroy(pattern);
|
||||
for (j = 0; j < length; j++) {
|
||||
if (hashTable[j] == hashValue)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the font is already registered then parse the next one */
|
||||
if (j != length)
|
||||
continue;
|
||||
|
||||
/* Register the font (i.e. append its hash value to the hash table) */
|
||||
if (!__glcArrayAppend(updatedHashTable, &hashValue)) {
|
||||
FcFontSetDestroy(fontSet);
|
||||
__glcArrayDestroy(updatedHashTable);
|
||||
return GL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
FcFontSetDestroy(fontSet);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
This->masterHashTable = updatedHashTable;
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Append a catalog to the context catalog list */
|
||||
void __glcContextAppendCatalog(__GLCcontext* This, const GLCchar* inCatalog)
|
||||
{
|
||||
#ifdef __WIN32__
|
||||
GLCchar8* duplicated = (GLCchar8*)_strdup((const char*)inCatalog);
|
||||
#else
|
||||
GLCchar8* duplicated = (GLCchar8*)strdup((const char*)inCatalog);
|
||||
#endif
|
||||
|
||||
if (!duplicated) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!__glcArrayAppend(This->catalogList, &duplicated)) {
|
||||
free(duplicated);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FcConfigAppFontAddDir(This->config, (const unsigned char*)inCatalog)) {
|
||||
__glcArrayRemove(This->catalogList, GLC_ARRAY_LENGTH(This->catalogList));
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
free(duplicated);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!__glcContextUpdateHashTable(This)) {
|
||||
/* For some reason the update of the master hash table has failed : the
|
||||
* new catalog must then be removed from GLC_CATALOG_LIST.
|
||||
*/
|
||||
__glcContextRemoveCatalog(This, GLC_ARRAY_LENGTH(This->catalogList));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Prepend a catalog to the context catalog list */
|
||||
void __glcContextPrependCatalog(__GLCcontext* This, const GLCchar* inCatalog)
|
||||
{
|
||||
#ifdef __WIN32__
|
||||
GLCchar8* duplicated = (GLCchar8*)_strdup((const char*)inCatalog);
|
||||
#else
|
||||
GLCchar8* duplicated = (GLCchar8*)strdup((const char*)inCatalog);
|
||||
#endif
|
||||
|
||||
if (!duplicated) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!__glcArrayInsert(This->catalogList, 0, &duplicated)) {
|
||||
free(duplicated);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FcConfigAppFontAddDir(This->config, (const unsigned char*)inCatalog)) {
|
||||
__glcArrayRemove(This->catalogList, 0);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
free(duplicated);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!__glcContextUpdateHashTable(This)) {
|
||||
/* For some reason the update of the master hash table has failed : the
|
||||
* new catalog must then be removed from GLC_CATALOG_LIST.
|
||||
*/
|
||||
__glcContextRemoveCatalog(This, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the path of the catalog identified by inIndex */
|
||||
GLCchar8* __glcContextGetCatalogPath(const __GLCcontext* This,
|
||||
const GLint inIndex)
|
||||
{
|
||||
if (inIndex >= GLC_ARRAY_LENGTH(This->catalogList)) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ((GLCchar8**)GLC_ARRAY_DATA(This->catalogList))[inIndex];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function must be called each time that a command destroys
|
||||
* a font. __glcContextDeleteFont removes, if necessary, the font identified by
|
||||
* inFont from the list GLC_CURRENT_FONT_LIST and then delete the font.
|
||||
*/
|
||||
void __glcContextDeleteFont(__GLCcontext* inContext, __GLCfont* font)
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
/* Look for the font into GLC_CURRENT_FONT_LIST */
|
||||
node = FT_List_Find(&inContext->currentFontList, font);
|
||||
|
||||
/* If the font has been found, remove it from the list */
|
||||
if (node) {
|
||||
FT_List_Remove(&inContext->currentFontList, node);
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFontClose(font);
|
||||
#endif
|
||||
__glcFree(node);
|
||||
}
|
||||
__glcFontDestroy(font, inContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove a catalog from the context catalog list */
|
||||
void __glcContextRemoveCatalog(__GLCcontext* This, const GLint inIndex)
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
GLCchar8* catalog = NULL;
|
||||
int i = 0;
|
||||
|
||||
if (inIndex >= GLC_ARRAY_LENGTH(This->catalogList)) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
FcConfigAppFontClear(This->config);
|
||||
catalog = ((GLCchar8**)GLC_ARRAY_DATA(This->catalogList))[inIndex];
|
||||
assert(catalog);
|
||||
__glcArrayRemove(This->catalogList, inIndex);
|
||||
free(catalog);
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(This->catalogList); i++) {
|
||||
catalog = ((GLCchar8**)GLC_ARRAY_DATA(This->catalogList))[i];
|
||||
assert(catalog);
|
||||
if (!FcConfigAppFontAddDir(This->config, catalog)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcArrayRemove(This->catalogList, i);
|
||||
free(catalog);
|
||||
/* After the removal of the problematic catalog , indices are shifted by 1
|
||||
*/
|
||||
if (i > 0) i--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-create the hash table from scratch */
|
||||
GLC_ARRAY_LENGTH(This->masterHashTable) = 0;
|
||||
__glcContextUpdateHashTable(This);
|
||||
|
||||
/* Remove from GLC_FONT_LIST the fonts that were defined in the catalog that
|
||||
* has been removed. This task has to be done even if the call to
|
||||
* __glcContextUpdateHashTable() has failed !!!
|
||||
*/
|
||||
for (node = This->fontList.head; node; node = node->next) {
|
||||
__GLCfont* font = (__GLCfont*)(node->data);
|
||||
GLCchar32 hashValue = 0;
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(This->masterHashTable);
|
||||
const int length = GLC_ARRAY_LENGTH(This->masterHashTable);
|
||||
__GLCmaster* master = __glcMasterCreate(font->parentMasterID, This);
|
||||
|
||||
if (!master)
|
||||
continue;
|
||||
|
||||
/* Check if the hash value of the master is in the hash table */
|
||||
hashValue = GLC_MASTER_HASH_VALUE(master);
|
||||
for (i = 0; i < length; i++) {
|
||||
if (hashValue == hashTable[i])
|
||||
break;
|
||||
}
|
||||
|
||||
/* The font is not contained in the hash table => remove it */
|
||||
if (i == length) {
|
||||
FT_List_Remove(&This->fontList, node);
|
||||
__glcContextDeleteFont(This, font);
|
||||
}
|
||||
|
||||
__glcMasterDestroy(master);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCcontext which is used to manage the contexts.
|
||||
*/
|
||||
|
||||
#ifndef __glc_ocontext_h
|
||||
#define __glc_ocontext_h
|
||||
|
||||
#ifndef __WIN32__
|
||||
#include <pthread.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#ifdef GLC_FT_CACHE
|
||||
#include FT_CACHE_H
|
||||
#endif
|
||||
#include FT_LIST_H
|
||||
|
||||
#include "oarray.h"
|
||||
#include "except.h"
|
||||
|
||||
#define GLC_MAX_MATRIX_STACK_DEPTH 32
|
||||
#define GLC_MAX_ATTRIB_STACK_DEPTH 16
|
||||
|
||||
typedef struct __GLCcontextRec __GLCcontext;
|
||||
typedef struct __GLCtextureRec __GLCtexture;
|
||||
typedef struct __GLCenableStateRec __GLCenableState;
|
||||
typedef struct __GLCrenderStateRec __GLCrenderState;
|
||||
typedef struct __GLCstringStateRec __GLCstringState;
|
||||
typedef struct __GLCglStateRec __GLCglState;
|
||||
typedef struct __GLCattribStackLevelRec __GLCattribStackLevel;
|
||||
typedef struct __GLCthreadAreaRec __GLCthreadArea;
|
||||
typedef struct __GLCcommonAreaRec __GLCcommonArea;
|
||||
typedef struct __GLCfontRec __GLCfont;
|
||||
|
||||
struct __GLCtextureRec {
|
||||
GLuint id;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
GLuint bufferObjectID;
|
||||
};
|
||||
|
||||
struct __GLCenableStateRec {
|
||||
GLboolean autoFont; /* GLC_AUTO_FONT */
|
||||
GLboolean glObjects; /* GLC_GLOBJECTS */
|
||||
GLboolean mipmap; /* GLC_MIPMAP */
|
||||
GLboolean hinting; /* GLC_HINTING_QSO */
|
||||
GLboolean extrude; /* GLC_EXTRUDE_QSO */
|
||||
GLboolean kerning; /* GLC_KERNING_QSO */
|
||||
};
|
||||
|
||||
struct __GLCrenderStateRec {
|
||||
GLfloat resolution; /* GLC_RESOLUTION */
|
||||
GLint renderStyle; /* GLC_RENDER_STYLE */
|
||||
GLfloat tolerance; /* GLC_PARAMETRIC_TOLERANCE_QSO */
|
||||
};
|
||||
|
||||
struct __GLCstringStateRec {
|
||||
GLint replacementCode; /* GLC_REPLACEMENT_CODE */
|
||||
GLint stringType; /* GLC_STRING_TYPE */
|
||||
GLCfunc callback; /* Callback function GLC_OP_glcUnmappedCode */
|
||||
GLvoid* dataPointer; /* GLC_DATA_POINTER */
|
||||
};
|
||||
|
||||
struct __GLCglStateRec {
|
||||
GLint textureID;
|
||||
GLint textureEnvMode;
|
||||
GLint pixelBufferObjectID;
|
||||
GLint vertexBufferObjectID;
|
||||
GLint elementBufferObjectID;
|
||||
GLboolean blend;
|
||||
GLboolean normalize;
|
||||
GLboolean vertexArray;
|
||||
GLboolean normalArray;
|
||||
GLboolean colorArray;
|
||||
GLboolean indexArray;
|
||||
GLboolean texCoordArray;
|
||||
GLboolean edgeFlagArray;
|
||||
GLint blendSrc;
|
||||
GLint blendDst;
|
||||
GLint vertexArraySize;
|
||||
GLint vertexArrayType;
|
||||
GLint vertexArrayStride;
|
||||
GLvoid* vertexArrayPointer;
|
||||
GLint texCoordArraySize;
|
||||
GLint texCoordArrayType;
|
||||
GLint texCoordArrayStride;
|
||||
GLvoid* texCoordArrayPointer;
|
||||
GLint unpackLsbFirst;
|
||||
GLint unpackRowLength;
|
||||
GLint unpackSkipPixels;
|
||||
GLint unpackSkipRows;
|
||||
GLint unpackAlignment;
|
||||
GLfloat colorBias[4];
|
||||
GLfloat colorScale[4];
|
||||
};
|
||||
|
||||
struct __GLCattribStackLevelRec {
|
||||
GLbitfield attribBits;
|
||||
__GLCrenderState renderState;
|
||||
__GLCstringState stringState;
|
||||
__GLCglState glState;
|
||||
__GLCenableState enableState;
|
||||
};
|
||||
|
||||
struct __GLCcontextRec {
|
||||
FT_ListNodeRec node;
|
||||
|
||||
GLCchar *buffer;
|
||||
size_t bufferSize;
|
||||
|
||||
FT_Library library;
|
||||
#ifdef GLC_FT_CACHE
|
||||
FTC_Manager cache;
|
||||
#endif
|
||||
FcConfig *config;
|
||||
|
||||
GLint id; /* Context ID */
|
||||
GLboolean isInGlobalCommand; /* Is in a global command ? */
|
||||
GLboolean pendingDelete; /* Is there a pending deletion ? */
|
||||
__GLCenableState enableState;
|
||||
__GLCrenderState renderState;
|
||||
__GLCstringState stringState;
|
||||
FT_ListRec currentFontList; /* GLC_CURRENT_FONT_LIST */
|
||||
FT_ListRec fontList; /* GLC_FONT_LIST */
|
||||
FT_ListRec genFontList; /* Fonts generated by glcGenFontID() */
|
||||
__GLCarray* masterHashTable;
|
||||
__GLCarray* catalogList; /* GLC_CATALOG_LIST */
|
||||
__GLCarray* measurementBuffer;
|
||||
GLfloat measurementStringBuffer[12];
|
||||
__GLCarray* vertexArray; /* Array of vertices */
|
||||
__GLCarray* controlPoints; /* Array of control points */
|
||||
__GLCarray* endContour; /* Array of contour limits */
|
||||
__GLCarray* vertexIndices; /* Array of vertex indices */
|
||||
__GLCarray* geomBatches; /* Array of geometric batches */
|
||||
|
||||
#ifdef GLEW_MX
|
||||
GLEWContext glewContext; /* GLEW context for OpenGL extensions */
|
||||
#endif
|
||||
__GLCtexture texture; /* Texture for immediate mode rendering */
|
||||
|
||||
__GLCtexture atlas;
|
||||
FT_ListRec atlasList;
|
||||
int atlasWidth;
|
||||
int atlasHeight;
|
||||
int atlasCount;
|
||||
|
||||
GLfloat* bitmapMatrix; /* GLC_BITMAP_MATRIX */
|
||||
GLfloat bitmapMatrixStack[4*GLC_MAX_MATRIX_STACK_DEPTH];
|
||||
GLint bitmapMatrixStackDepth;
|
||||
|
||||
__GLCattribStackLevel attribStack[GLC_MAX_ATTRIB_STACK_DEPTH];
|
||||
GLint attribStackDepth;
|
||||
|
||||
GLboolean isCurrent;
|
||||
GLboolean isInCallbackFunc; /* Is a callback function executing ? */
|
||||
};
|
||||
|
||||
struct __GLCthreadAreaRec {
|
||||
__GLCcontext* currentContext;
|
||||
GLCenum errorState;
|
||||
GLint lockState;
|
||||
FT_ListRec exceptionStack;
|
||||
__glcException failedTry;
|
||||
};
|
||||
|
||||
struct __GLCcommonAreaRec {
|
||||
GLint versionMajor; /* GLC_VERSION_MAJOR */
|
||||
GLint versionMinor; /* GLC_VERSION_MINOR */
|
||||
|
||||
FT_ListRec contextList;
|
||||
#ifndef __WIN32__
|
||||
pthread_mutex_t mutex; /* For concurrent accesses to the common
|
||||
area */
|
||||
#ifndef HAVE_TLS
|
||||
pthread_key_t threadKey;
|
||||
pthread_t threadID;
|
||||
pthread_once_t __glcInitThreadOnce;
|
||||
#endif /* HAVE_TLS */
|
||||
#else /* __WIN32__ */
|
||||
CRITICAL_SECTION section;
|
||||
DWORD threadKey;
|
||||
DWORD threadID;
|
||||
LONG __glcInitThreadOnce;
|
||||
#endif
|
||||
|
||||
/* Evil hack : we use the FT_MemoryRec_ structure definition which is
|
||||
* supposed not to be exported by FreeType headers. So this definition may
|
||||
* fail if the guys of FreeType decide not to expose FT_MemoryRec_ anymore.
|
||||
* However, this has not happened yet so we still rely on FT_MemoryRec_ ...
|
||||
*/
|
||||
struct FT_MemoryRec_ memoryManager;
|
||||
};
|
||||
|
||||
extern __GLCcommonArea __glcCommonArea;
|
||||
#ifdef HAVE_TLS
|
||||
extern __thread __GLCthreadArea __glcTlsThreadArea
|
||||
__attribute__((tls_model("initial-exec")));
|
||||
#else
|
||||
extern __GLCthreadArea* __glcThreadArea;
|
||||
#endif
|
||||
|
||||
__GLCcontext* __glcContextCreate(const GLint inContext);
|
||||
void __glcContextDestroy(__GLCcontext *This);
|
||||
__GLCfont* __glcContextGetFont(__GLCcontext *This, const GLint code);
|
||||
GLCchar* __glcContextQueryBuffer(__GLCcontext *This, const size_t inSize);
|
||||
void __glcContextAppendCatalog(__GLCcontext* This, const GLCchar* inCatalog);
|
||||
void __glcContextPrependCatalog(__GLCcontext* This, const GLCchar* inCatalog);
|
||||
void __glcContextRemoveCatalog(__GLCcontext* This, const GLint inIndex);
|
||||
GLCchar8* __glcContextGetCatalogPath(const __GLCcontext* This,
|
||||
const GLint inIndex);
|
||||
void __glcContextDeleteFont(__GLCcontext* inContext, __GLCfont* font);
|
||||
#endif /* __glc_ocontext_h */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,106 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCfaceDescriptor that contains the description of a
|
||||
* face.
|
||||
*/
|
||||
|
||||
#ifndef __glc_ofacedesc_h
|
||||
#define __glc_ofacedesc_h
|
||||
|
||||
#include "omaster.h"
|
||||
|
||||
typedef struct __GLCrendererDataRec __GLCrendererData;
|
||||
typedef struct __GLCfaceDescriptorRec __GLCfaceDescriptor;
|
||||
|
||||
struct __GLCfaceDescriptorRec {
|
||||
FT_ListNodeRec node;
|
||||
FcPattern* pattern;
|
||||
FT_Face face;
|
||||
#ifndef GLC_FT_CACHE
|
||||
int faceRefCount;
|
||||
#endif
|
||||
FT_ListRec glyphList;
|
||||
};
|
||||
|
||||
|
||||
__GLCfaceDescriptor* __glcFaceDescCreate(const __GLCmaster* inMaster,
|
||||
const GLCchar8* inFace,
|
||||
const __GLCcontext* inContext,
|
||||
const GLint inCode);
|
||||
void __glcFaceDescDestroy(__GLCfaceDescriptor* This, __GLCcontext* inContext);
|
||||
#ifndef GLC_FT_CACHE
|
||||
FT_Face __glcFaceDescOpen(__GLCfaceDescriptor* This,
|
||||
__GLCcontext* inContext);
|
||||
void __glcFaceDescClose(__GLCfaceDescriptor* This);
|
||||
#endif
|
||||
__GLCglyph* __glcFaceDescGetGlyph(__GLCfaceDescriptor* This,
|
||||
const GLint inCode,
|
||||
const __GLCcontext* inContext);
|
||||
void __glcFaceDescDestroyGLObjects(const __GLCfaceDescriptor* This,
|
||||
__GLCcontext* inContext);
|
||||
GLboolean __glcFaceDescPrepareGlyph(__GLCfaceDescriptor* This,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY,
|
||||
const GLCulong inGlyphIndex);
|
||||
GLfloat* __glcFaceDescGetBoundingBox(__GLCfaceDescriptor* This,
|
||||
const GLCulong inGlyphIndex,
|
||||
GLfloat* outVec, const GLfloat inScaleX,
|
||||
const GLfloat inScaleY,
|
||||
const __GLCcontext* inContext);
|
||||
GLfloat* __glcFaceDescGetAdvance(__GLCfaceDescriptor* This,
|
||||
const GLCulong inGlyphIndex, GLfloat* outVec,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY,
|
||||
const __GLCcontext* inContext);
|
||||
const GLCchar8* __glcFaceDescGetFontFormat(const __GLCfaceDescriptor* This,
|
||||
const __GLCcontext* inContext,
|
||||
const GLCenum inAttrib);
|
||||
GLfloat* __glcFaceDescGetMaxMetric(__GLCfaceDescriptor* This, GLfloat* outVec,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY);
|
||||
GLfloat* __glcFaceDescGetKerning(__GLCfaceDescriptor* This,
|
||||
const GLCuint inGlyphIndex,
|
||||
const GLCuint inPrevGlyphIndex,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY,
|
||||
GLfloat* outVec,
|
||||
const __GLCcontext* inContext);
|
||||
GLCchar8* __glcFaceDescGetStyleName(__GLCfaceDescriptor* This);
|
||||
GLboolean __glcFaceDescIsFixedPitch(__GLCfaceDescriptor* This);
|
||||
GLboolean __glcFaceDescOutlineDecompose(const __GLCfaceDescriptor* This,
|
||||
__GLCrendererData* inData,
|
||||
const __GLCcontext* inContext);
|
||||
GLboolean __glcFaceDescGetBitmapSize(const __GLCfaceDescriptor* This,
|
||||
GLint* outWidth, GLint *outHeight,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY,
|
||||
GLint* outPixBoundingBox,
|
||||
const int inFactor,
|
||||
const __GLCcontext* inContext);
|
||||
GLboolean __glcFaceDescGetBitmap(const __GLCfaceDescriptor* This,
|
||||
const GLint inWidth, const GLint inHeight,
|
||||
const void* inBuffer,
|
||||
const __GLCcontext* inContext);
|
||||
GLboolean __glcFaceDescOutlineEmpty(__GLCfaceDescriptor* This);
|
||||
__GLCcharMap* __glcFaceDescGetCharMap(__GLCfaceDescriptor* This,
|
||||
__GLCcontext* inContext);
|
||||
#endif /* __glc_ofacedesc_h */
|
|
@ -0,0 +1,353 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/* Defines the methods of an object that is intended to managed fonts */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCfont which manage the fonts
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
* The user must give the master 'inParent' which the font will instantiate.
|
||||
*/
|
||||
__GLCfont* __glcFontCreate(GLint inID, __GLCmaster* inMaster,
|
||||
__GLCcontext* inContext, GLint inCode)
|
||||
{
|
||||
__GLCfont *This = NULL;
|
||||
|
||||
assert(inContext);
|
||||
|
||||
This = (__GLCfont*)__glcMalloc(sizeof(__GLCfont));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCfont));
|
||||
|
||||
if (inMaster) {
|
||||
/* At font creation, the default face is the first one.
|
||||
* glcFontFace() can change the face.
|
||||
*/
|
||||
This->faceDesc = __glcFaceDescCreate(inMaster, NULL, inContext, inCode);
|
||||
if (!This->faceDesc) {
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->charMap = __glcFaceDescGetCharMap(This->faceDesc, inContext);
|
||||
if (!This->charMap) {
|
||||
__glcFaceDescDestroy(This->faceDesc, inContext);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->parentMasterID = __glcMasterGetID(inMaster, inContext);
|
||||
}
|
||||
else {
|
||||
/* Creates an empty font (used by glcGenFontID() to reserve font IDs) */
|
||||
This->faceDesc = NULL;
|
||||
This->charMap = NULL;
|
||||
This->parentMasterID = 0;
|
||||
}
|
||||
|
||||
This->id = inID;
|
||||
This->maxMetricCached = GL_FALSE;
|
||||
memset(This->maxMetric, 0, 6 * sizeof(GLfloat));
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcFontDestroy(__GLCfont *This, __GLCcontext* inContext)
|
||||
{
|
||||
if (This->charMap)
|
||||
__glcCharMapDestroy(This->charMap);
|
||||
|
||||
if (This->faceDesc)
|
||||
__glcFaceDescDestroy(This->faceDesc, inContext);
|
||||
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Extract from the font the glyph which corresponds to the character code
|
||||
* 'inCode'.
|
||||
*/
|
||||
__GLCglyph* __glcFontGetGlyph(const __GLCfont *This, const GLint inCode,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
/* Try to get the glyph from the character map */
|
||||
__GLCglyph* glyph = __glcCharMapGetGlyph(This->charMap, inCode);
|
||||
|
||||
if (!glyph) {
|
||||
/* If it fails, we must extract the glyph from the face */
|
||||
glyph = __glcFaceDescGetGlyph(This->faceDesc, inCode, inContext);
|
||||
if (!glyph)
|
||||
return NULL;
|
||||
|
||||
/* Update the character map so that the glyph will be cached */
|
||||
__glcCharMapAddChar(This->charMap, inCode, glyph);
|
||||
}
|
||||
|
||||
return glyph;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the bounding box of a glyph according to the size given by inScaleX and
|
||||
* inScaleY. The result is returned in outVec. 'inCode' contains the character
|
||||
* code for which the bounding box is requested.
|
||||
*/
|
||||
GLfloat* __glcFontGetBoundingBox(const __GLCfont *This, const GLint inCode,
|
||||
GLfloat* outVec, const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY)
|
||||
{
|
||||
/* Get the glyph from the font */
|
||||
__GLCglyph* glyph = __glcFontGetGlyph(This, inCode, inContext);
|
||||
|
||||
assert(outVec);
|
||||
|
||||
if (!glyph)
|
||||
return NULL;
|
||||
|
||||
/* If the bounding box of the glyph is cached then copy it to outVec and
|
||||
* return.
|
||||
*/
|
||||
if (glyph->boundingBoxCached && inContext->enableState.glObjects) {
|
||||
memcpy(outVec, glyph->boundingBox, 4 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
|
||||
/* Otherwise, we must extract the bounding box from the face file */
|
||||
if (!__glcFaceDescGetBoundingBox(This->faceDesc, glyph->index, outVec,
|
||||
inScaleX, inScaleY, inContext))
|
||||
return NULL;
|
||||
|
||||
/* Special case for glyphes which have no bounding box (i.e. spaces) */
|
||||
if ((fabs(outVec[0] - outVec[2]) < GLC_EPSILON)
|
||||
|| (fabs(outVec[1] - outVec[3]) < GLC_EPSILON)) {
|
||||
GLfloat advance[2] = {0.f, 0.f};
|
||||
|
||||
if (__glcFontGetAdvance(This, inCode, advance, inContext, inScaleX,
|
||||
inScaleY)) {
|
||||
if (fabs(outVec[0] - outVec[2]) < GLC_EPSILON)
|
||||
outVec[2] += advance[0];
|
||||
|
||||
if (fabs(outVec[1] - outVec[3]) < GLC_EPSILON)
|
||||
outVec[3] += advance[1];
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy the result to outVec and return */
|
||||
if (inContext->enableState.glObjects) {
|
||||
memcpy(glyph->boundingBox, outVec, 4 * sizeof(GLfloat));
|
||||
glyph->boundingBoxCached = GL_TRUE;
|
||||
}
|
||||
|
||||
return outVec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the advance of a glyph according to the size given by inScaleX and
|
||||
* inScaleY. The result is returned in outVec. 'inCode' contains the character
|
||||
* code for which the advance is requested.
|
||||
*/
|
||||
GLfloat* __glcFontGetAdvance(const __GLCfont* This, const GLint inCode,
|
||||
GLfloat* outVec, const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY)
|
||||
{
|
||||
/* Get the glyph from the font */
|
||||
__GLCglyph* glyph = __glcFontGetGlyph(This, inCode, inContext);
|
||||
|
||||
assert(outVec);
|
||||
|
||||
if (!glyph)
|
||||
return NULL;
|
||||
|
||||
/* If the advance of the glyph is cached then copy it to outVec and
|
||||
* return.
|
||||
*/
|
||||
if (glyph->advanceCached && inContext->enableState.glObjects) {
|
||||
memcpy(outVec, glyph->advance, 2 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
|
||||
/* Otherwise, we must extract the advance from the face file */
|
||||
if (!__glcFaceDescGetAdvance(This->faceDesc, glyph->index, outVec, inScaleX,
|
||||
inScaleY, inContext))
|
||||
return NULL;
|
||||
|
||||
/* Copy the result to outVec and return */
|
||||
if (inContext->enableState.glObjects) {
|
||||
memcpy(glyph->advance, outVec, 2 * sizeof(GLfloat));
|
||||
glyph->advanceCached = GL_TRUE;
|
||||
}
|
||||
|
||||
return outVec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the maximum metrics of a face that is the bounding box that encloses
|
||||
* every glyph of the face, and the maximum advance of the face.
|
||||
*/
|
||||
GLfloat* __glcFontGetMaxMetric(__GLCfont* This, GLfloat* outVec,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY)
|
||||
{
|
||||
assert(outVec);
|
||||
|
||||
if (This->maxMetricCached && inContext->enableState.glObjects) {
|
||||
memcpy(outVec, This->maxMetric, 6 * sizeof(GLfloat));
|
||||
return outVec;
|
||||
}
|
||||
|
||||
if (!__glcFaceDescGetMaxMetric(This->faceDesc, outVec, inContext, inScaleX,
|
||||
inScaleY))
|
||||
return NULL;
|
||||
|
||||
/* Copy the result to outVec and return */
|
||||
if (inContext->enableState.glObjects) {
|
||||
memcpy(This->maxMetric, outVec, 6 * sizeof(GLfloat));
|
||||
This->maxMetricCached = GL_TRUE;
|
||||
}
|
||||
|
||||
return outVec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the kerning information of a pair of glyph according to the size given by
|
||||
* inScaleX and inScaleY. The result is returned in outVec. 'inCode' contains
|
||||
* the current character code and 'inPrevCode' the character code of the
|
||||
* previously displayed character.
|
||||
*/
|
||||
GLfloat* __glcFontGetKerning(const __GLCfont* This, const GLint inCode,
|
||||
const GLint inPrevCode, GLfloat* outVec,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY)
|
||||
{
|
||||
__GLCglyph* glyph = __glcFontGetGlyph(This, inCode, inContext);
|
||||
__GLCglyph* prevGlyph = __glcFontGetGlyph(This, inPrevCode, inContext);
|
||||
|
||||
if (!glyph || !prevGlyph)
|
||||
return NULL;
|
||||
|
||||
return __glcFaceDescGetKerning(This->faceDesc, glyph->index, prevGlyph->index,
|
||||
inScaleX, inScaleY, outVec, inContext);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This internal function tries to open the face file which name is identified
|
||||
* by 'inFace'. If it succeeds, it closes the previous face and stores the new
|
||||
* face attributes in the __GLCfont object "inFont". Otherwise, it leaves the
|
||||
* font unchanged. GL_TRUE or GL_FALSE are returned to indicate if the function
|
||||
* succeeded or not.
|
||||
*/
|
||||
GLboolean __glcFontFace(__GLCfont* This, const GLCchar8* inFace,
|
||||
__GLCcontext *inContext)
|
||||
{
|
||||
__GLCfaceDescriptor *faceDesc = NULL;
|
||||
__GLCmaster *master = NULL;
|
||||
__GLCcharMap* newCharMap = NULL;
|
||||
|
||||
/* TODO : Verify if the font has already the required face activated */
|
||||
|
||||
master = __glcMasterCreate(This->parentMasterID, inContext);
|
||||
if (!master)
|
||||
return GL_FALSE;
|
||||
|
||||
/* Get the face descriptor of the face identified by the string inFace */
|
||||
faceDesc = __glcFaceDescCreate(master, inFace, inContext, 0);
|
||||
if (!faceDesc) {
|
||||
__glcMasterDestroy(master);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
newCharMap = __glcFaceDescGetCharMap(faceDesc, inContext);
|
||||
if (!newCharMap) {
|
||||
__glcFaceDescDestroy(faceDesc, inContext);
|
||||
__glcMasterDestroy(master);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
__glcMasterDestroy(master);
|
||||
|
||||
#ifndef GLC_FT_CACHE
|
||||
/* If the font belongs to GLC_CURRENT_FONT_LIST then open the font file */
|
||||
if (FT_List_Find(&inContext->currentFontList, This)) {
|
||||
|
||||
/* Open the new face */
|
||||
if (!__glcFaceDescOpen(faceDesc, inContext)) {
|
||||
__glcFaceDescDestroy(faceDesc, inContext);
|
||||
__glcCharMapDestroy(newCharMap);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
/* Close the current face */
|
||||
__glcFontClose(This);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Destroy the current charmap */
|
||||
if (This->charMap)
|
||||
__glcCharMapDestroy(This->charMap);
|
||||
|
||||
This->charMap = newCharMap;
|
||||
|
||||
__glcFaceDescDestroy(This->faceDesc, inContext);
|
||||
This->faceDesc = faceDesc;
|
||||
This->maxMetricCached = GL_FALSE;
|
||||
memset(This->maxMetric, 0, 6 * sizeof(GLfloat));
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Load a glyph of the current font face and stores the corresponding data in
|
||||
* the corresponding face. The size of the glyph is given by inScaleX and
|
||||
* inScaleY. 'inGlyphIndex' contains the index of the glyph in the font file.
|
||||
*/
|
||||
GLboolean __glcFontPrepareGlyph(const __GLCfont* This,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY,
|
||||
const GLCulong inGlyphIndex)
|
||||
{
|
||||
GLboolean result = __glcFaceDescPrepareGlyph(This->faceDesc, inContext,
|
||||
inScaleX, inScaleY,
|
||||
inGlyphIndex);
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFaceDescClose(This->faceDesc);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCfont which manage the fonts
|
||||
*/
|
||||
|
||||
#ifndef __glc_ofont_h
|
||||
#define __glc_ofont_h
|
||||
|
||||
#include "ofacedesc.h"
|
||||
|
||||
/* It seems that Visual C++ does not recognize the inline keyword !?! */
|
||||
#ifdef _MSC_VER
|
||||
#define inline
|
||||
#endif
|
||||
|
||||
struct __GLCfontRec {
|
||||
GLint id;
|
||||
__GLCfaceDescriptor* faceDesc;
|
||||
GLint parentMasterID;
|
||||
__GLCcharMap* charMap;
|
||||
GLfloat maxMetric[6];
|
||||
GLboolean maxMetricCached;
|
||||
};
|
||||
|
||||
__GLCfont* __glcFontCreate(GLint id, __GLCmaster* inMaster,
|
||||
__GLCcontext* inContext, GLint inCode);
|
||||
void __glcFontDestroy(__GLCfont *This, __GLCcontext* inContext);
|
||||
__GLCglyph* __glcFontGetGlyph(const __GLCfont *This, const GLint inCode,
|
||||
const __GLCcontext* inContext);
|
||||
GLfloat* __glcFontGetBoundingBox(const __GLCfont *This, const GLint inCode,
|
||||
GLfloat* outVec, const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY);
|
||||
GLfloat* __glcFontGetAdvance(const __GLCfont *This, const GLint inCode,
|
||||
GLfloat* outVec, const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY);
|
||||
GLfloat* __glcFontGetKerning(const __GLCfont* This, const GLint inCode,
|
||||
const GLint inPrevCode, GLfloat* outVec,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY);
|
||||
GLboolean __glcFontPrepareGlyph(const __GLCfont* This,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY,
|
||||
const GLCulong inGlyphIndex);
|
||||
GLfloat* __glcFontGetMaxMetric(__GLCfont* This, GLfloat* outVec,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY);
|
||||
|
||||
/* Inline functions definitions */
|
||||
|
||||
#ifndef GLC_FT_CACHE
|
||||
/* Open the font file */
|
||||
static inline void* __glcFontOpen(__GLCfont* This, __GLCcontext* inContext)
|
||||
{
|
||||
return __glcFaceDescOpen(This->faceDesc, inContext);
|
||||
}
|
||||
|
||||
/* Close the font file */
|
||||
static inline void __glcFontClose(__GLCfont* This)
|
||||
{
|
||||
__glcFaceDescClose(This->faceDesc);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get the size of the bitmap in which the glyph will be rendered */
|
||||
static inline GLboolean __glcFontGetBitmapSize(const __GLCfont* This,
|
||||
GLint* outWidth,
|
||||
GLint* outHeight,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY,
|
||||
const int inFactor,
|
||||
GLint* outPixBoundingBox,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
return __glcFaceDescGetBitmapSize(This->faceDesc, outWidth, outHeight,
|
||||
inScaleX, inScaleY, outPixBoundingBox,
|
||||
inFactor, inContext);
|
||||
}
|
||||
|
||||
/* Decompose the outline of a glyph */
|
||||
static inline GLboolean __glcFontOutlineDecompose(const __GLCfont* This,
|
||||
__GLCrendererData* inData,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
return __glcFaceDescOutlineDecompose(This->faceDesc, inData, inContext);
|
||||
}
|
||||
|
||||
/* Render the glyph in a bitmap */
|
||||
static inline GLboolean __glcFontGetBitmap(const __GLCfont* This,
|
||||
const GLint inWidth,
|
||||
const GLint inHeight,
|
||||
const void* inBuffer,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
return __glcFaceDescGetBitmap(This->faceDesc, inWidth, inHeight, inBuffer,
|
||||
inContext);
|
||||
}
|
||||
|
||||
/* Chek if the outline of the glyph is empty (which means it is a spacing
|
||||
* character).
|
||||
*/
|
||||
static inline GLboolean __glcFontOutlineEmpty(const __GLCfont* This)
|
||||
{
|
||||
return __glcFaceDescOutlineEmpty(This->faceDesc);
|
||||
}
|
||||
|
||||
static inline GLboolean __glcFontHasChar(const __GLCfont* This,
|
||||
const GLint inCode)
|
||||
{
|
||||
return __glcCharMapHasChar(This->charMap, inCode);
|
||||
}
|
||||
|
||||
#endif /* __glc_ofont_h */
|
|
@ -0,0 +1,233 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/* Defines the methods of an object that is intended to managed glyphs */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCglyph which caches all the data needed for a given
|
||||
* glyph : display list, texture, bounding box, advance, index in the font
|
||||
* file, etc.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
* The user must give the index of the glyph in the font file and its Unicode
|
||||
* codepoint.
|
||||
*/
|
||||
__GLCglyph* __glcGlyphCreate(const GLCulong inIndex, const GLCulong inCode)
|
||||
{
|
||||
__GLCglyph* This = NULL;
|
||||
|
||||
This = (__GLCglyph*)__glcMalloc(sizeof(__GLCglyph));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCglyph));
|
||||
|
||||
This->node.data = This;
|
||||
This->index = inIndex;
|
||||
This->codepoint = inCode;
|
||||
This->isSpacingChar = GL_FALSE;
|
||||
This->advanceCached = GL_FALSE;
|
||||
This->boundingBoxCached = GL_FALSE;
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcGlyphDestroy(__GLCglyph* This, __GLCcontext* inContext)
|
||||
{
|
||||
__glcGlyphDestroyGLObjects(This, inContext);
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove all GL objects related to the texture of the glyph */
|
||||
void __glcGlyphDestroyTexture(__GLCglyph* This, const __GLCcontext* inContext)
|
||||
{
|
||||
if (!inContext->isInGlobalCommand && !GLEW_ARB_vertex_buffer_object)
|
||||
glDeleteLists(This->glObject[1], 1);
|
||||
This->glObject[1] = 0;
|
||||
This->textureObject = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function destroys the display lists and the texture objects that
|
||||
* are associated with a glyph.
|
||||
*/
|
||||
void __glcGlyphDestroyGLObjects(__GLCglyph* This, __GLCcontext* inContext)
|
||||
{
|
||||
if (This->glObject[1]) {
|
||||
__glcReleaseAtlasElement(This->textureObject, inContext);
|
||||
__glcGlyphDestroyTexture(This, inContext);
|
||||
}
|
||||
|
||||
if (!inContext->isInGlobalCommand) {
|
||||
if (This->glObject[0]) {
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
glDeleteBuffersARB(1, &This->glObject[0]);
|
||||
if (This->contours)
|
||||
__glcFree(This->contours);
|
||||
This->nContour = 0;
|
||||
This->contours = NULL;
|
||||
}
|
||||
else
|
||||
glDeleteLists(This->glObject[0], 1);
|
||||
}
|
||||
|
||||
if (This->glObject[2]) {
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
glDeleteBuffersARB(1, &This->glObject[2]);
|
||||
if (This->geomBatches)
|
||||
__glcFree(This->geomBatches);
|
||||
This->nGeomBatch = 0;
|
||||
This->geomBatches = NULL;
|
||||
}
|
||||
else
|
||||
glDeleteLists(This->glObject[2], 1);
|
||||
}
|
||||
|
||||
if (This->glObject[3]) {
|
||||
if (GLEW_ARB_vertex_buffer_object)
|
||||
glDeleteBuffersARB(1, &This->glObject[3]);
|
||||
else
|
||||
glDeleteLists(This->glObject[3], 1);
|
||||
}
|
||||
|
||||
memset(This->glObject, 0, 4 * sizeof(GLuint));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Returns the number of display that has been built for a glyph */
|
||||
int __glcGlyphGetDisplayListCount(const __GLCglyph* This)
|
||||
{
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
|
||||
if (GLEW_ARB_vertex_buffer_object)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (This->glObject[i])
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Returns the ID of the inCount-th display list that has been built for a
|
||||
* glyph.
|
||||
*/
|
||||
GLuint __glcGlyphGetDisplayList(const __GLCglyph* This, const int inCount)
|
||||
{
|
||||
int i = 0;
|
||||
int count = inCount;
|
||||
|
||||
assert(inCount >= 0);
|
||||
assert(inCount < __glcGlyphGetDisplayListCount(This));
|
||||
|
||||
if (GLEW_ARB_vertex_buffer_object)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
GLuint displayList = This->glObject[i];
|
||||
|
||||
if (displayList) {
|
||||
if (!count)
|
||||
return displayList;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* The program is not supposed to reach the end of the function.
|
||||
* The following return is there to prevent the compiler to issue
|
||||
* a warning about 'control reaching the end of a non-void function'.
|
||||
*/
|
||||
return 0xdeadbeef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Returns the number of buffer objects that has been built for a glyph */
|
||||
int __glcGlyphGetBufferObjectCount(const __GLCglyph* This)
|
||||
{
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
|
||||
assert(GLEW_ARB_vertex_buffer_object);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (i == 1)
|
||||
continue;
|
||||
|
||||
if (This->glObject[i])
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Returns the ID of the inCount-th buffer object that has been built for a
|
||||
* glyph.
|
||||
*/
|
||||
GLuint __glcGlyphGetBufferObject(const __GLCglyph* This, const int inCount)
|
||||
{
|
||||
int i = 0;
|
||||
int count = inCount;
|
||||
|
||||
assert(GLEW_ARB_vertex_buffer_object);
|
||||
assert(inCount >= 0);
|
||||
assert(inCount < __glcGlyphGetBufferObjectCount(This));
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
GLuint bufferObject = This->glObject[i];
|
||||
|
||||
if (i == 1)
|
||||
continue;
|
||||
|
||||
if (bufferObject) {
|
||||
if (!count)
|
||||
return bufferObject;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
/* The program is not supposed to reach the end of the function.
|
||||
* The following return is there to prevent the compiler to issue
|
||||
* a warning about 'control reaching the end of a non-void function'.
|
||||
*/
|
||||
return 0xdeadbeef;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCglyph which caches all the data needed for a given
|
||||
* glyph : display list, texture, bounding box, advance, index in the font
|
||||
* file, etc.
|
||||
*/
|
||||
|
||||
#ifndef __glc_oglyph_h
|
||||
#define __glc_oglyph_h
|
||||
|
||||
typedef struct __GLCglyphRec __GLCglyph;
|
||||
typedef struct __GLCatlasElementRec __GLCatlasElement;
|
||||
typedef struct __GLCgeomBatchRec __GLCgeomBatch;
|
||||
|
||||
struct __GLCglyphRec {
|
||||
FT_ListNodeRec node;
|
||||
|
||||
GLCulong index;
|
||||
GLCulong codepoint;
|
||||
/* GL objects management */
|
||||
__GLCatlasElement* textureObject;
|
||||
GLuint glObject[4];
|
||||
GLint nContour;
|
||||
GLint* contours;
|
||||
GLint nGeomBatch;
|
||||
__GLCgeomBatch* geomBatches;
|
||||
/* Measurement infos */
|
||||
GLfloat boundingBox[4];
|
||||
GLfloat advance[2];
|
||||
GLboolean advanceCached;
|
||||
GLboolean boundingBoxCached;
|
||||
GLboolean isSpacingChar;
|
||||
};
|
||||
|
||||
__GLCglyph* __glcGlyphCreate(const GLCulong inIndex, const GLCulong inCode);
|
||||
void __glcGlyphDestroy(__GLCglyph* This, __GLCcontext* inContext);
|
||||
void __glcGlyphDestroyTexture(__GLCglyph* This, const __GLCcontext* inContext);
|
||||
void __glcGlyphDestroyGLObjects(__GLCglyph* This, __GLCcontext* inContext);
|
||||
int __glcGlyphGetDisplayListCount(const __GLCglyph* This);
|
||||
GLuint __glcGlyphGetDisplayList(const __GLCglyph* This, const int inCount);
|
||||
int __glcGlyphGetBufferObjectCount(const __GLCglyph* This);
|
||||
GLuint __glcGlyphGetBufferObject(const __GLCglyph* This, const int inCount);
|
||||
#endif
|
|
@ -0,0 +1,618 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCmaster which manage the masters
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef GLboolean (*__glcPatternProcess)(FcFontSet* inFontSet,
|
||||
FcPattern* inPattern, int inFont,
|
||||
void* inData);
|
||||
struct __GLCdataRec {
|
||||
const __GLCmaster* master;
|
||||
GLint* index;
|
||||
};
|
||||
typedef struct __GLCdataRec __GLCdata;
|
||||
|
||||
|
||||
static int __glcScanFonts(const __GLCcontext* inContext, FcFontSet** outFontSet,
|
||||
FcPattern** outPattern,
|
||||
__glcPatternProcess inPatternProcess, void* inData)
|
||||
{
|
||||
int font = 0;
|
||||
FcObjectSet* objectSet = NULL;
|
||||
FcFontSet *fontSet = NULL;
|
||||
FcPattern *pattern = FcPatternCreate();
|
||||
|
||||
/* Use Fontconfig to get the default font files */
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return -1;
|
||||
}
|
||||
objectSet = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, FC_OUTLINE, FC_SPACING,
|
||||
FC_STYLE, NULL);
|
||||
if (!objectSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcPatternDestroy(pattern);
|
||||
return -1;
|
||||
}
|
||||
fontSet = FcFontList(inContext->config, pattern, objectSet);
|
||||
FcObjectSetDestroy(objectSet);
|
||||
FcPatternDestroy(pattern);
|
||||
if (!fontSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Parse the font set looking for a font with an outline and which hash value
|
||||
* matches the hash value of the master we are looking for.
|
||||
*/
|
||||
for (font = 0; font < fontSet->nfont; font++) {
|
||||
FcBool outline = FcFalse;
|
||||
FcChar8* family = NULL;
|
||||
int fixed = 0;
|
||||
FcChar8* foundry = NULL;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcResultMatch;
|
||||
|
||||
result = FcPatternGetBool(fontSet->fonts[font], FC_OUTLINE, 0, &outline);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetBool(fontSet->fonts[font], FC_OUTLINE, 0, &outline);
|
||||
#endif
|
||||
if (!outline)
|
||||
continue;
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(fontSet->fonts[font], FC_FAMILY, 0, &family);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetString(fontSet->fonts[font], FC_FOUNDRY, 0, &foundry);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetInteger(fontSet->fonts[font], FC_SPACING, 0, &fixed);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(fontSet->fonts[font], FC_FAMILY, 0, &family);
|
||||
FcPatternGetString(fontSet->fonts[font], FC_FOUNDRY, 0, &foundry);
|
||||
FcPatternGetInteger(fontSet->fonts[font], FC_SPACING, 0, &fixed);
|
||||
#endif
|
||||
|
||||
if (foundry)
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
|
||||
FC_FOUNDRY, FcTypeString, foundry, FC_SPACING,
|
||||
FcTypeInteger, fixed, NULL);
|
||||
else
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
|
||||
FC_SPACING, FcTypeInteger, fixed, NULL);
|
||||
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (inPatternProcess(fontSet, pattern, font, inData)) {
|
||||
*outPattern = pattern;
|
||||
*outFontSet = fontSet;
|
||||
return font;
|
||||
}
|
||||
|
||||
FcPatternDestroy(pattern);
|
||||
}
|
||||
|
||||
*outPattern = NULL;
|
||||
*outFontSet = fontSet;
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GLboolean __glcCheckHashValue(FcFontSet* GLC_UNUSED_ARG(inFontSet),
|
||||
FcPattern* inPattern,
|
||||
int GLC_UNUSED_ARG(inFont), void* inData)
|
||||
{
|
||||
GLCchar32 hashValue = *((GLCchar32*)inData);
|
||||
|
||||
if (hashValue == FcPatternHash(inPattern))
|
||||
return GL_TRUE;
|
||||
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
*/
|
||||
__GLCmaster* __glcMasterCreate(const GLint inMaster,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
__GLCmaster* This = NULL;
|
||||
GLCchar32 hashValue =
|
||||
((GLCchar32*)GLC_ARRAY_DATA(inContext->masterHashTable))[inMaster];
|
||||
FcFontSet *fontSet = NULL;
|
||||
FcPattern *pattern = NULL;
|
||||
int font = __glcScanFonts(inContext, &fontSet, &pattern, __glcCheckHashValue,
|
||||
&hashValue);
|
||||
|
||||
assert(font < fontSet->nfont);
|
||||
|
||||
FcFontSetDestroy(fontSet);
|
||||
if (font < 0)
|
||||
return NULL;
|
||||
|
||||
This = (__GLCmaster*)__glcMalloc(sizeof(__GLCmaster));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcPatternDestroy(pattern);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCmaster));
|
||||
|
||||
/* Duplicate the pattern of the found font (otherwise it will be deleted with
|
||||
* the font set).
|
||||
*/
|
||||
This->pattern = pattern;
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcMasterDestroy(__GLCmaster* This)
|
||||
{
|
||||
FcPatternDestroy(This->pattern);
|
||||
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GLboolean __glcFindMasterIndex(FcFontSet* GLC_UNUSED_ARG(inFontSet),
|
||||
FcPattern* inPattern,
|
||||
int GLC_UNUSED_ARG(inFont), void* inData)
|
||||
{
|
||||
const __GLCmaster* This = ((__GLCdata*)inData)->master;
|
||||
GLint *idx = ((__GLCdata*)inData)->index;
|
||||
FcBool equal = FcPatternEqual(inPattern, This->pattern);
|
||||
|
||||
if (equal) {
|
||||
if (*idx)
|
||||
(*idx)--;
|
||||
else
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the style name of the face identified by inIndex */
|
||||
GLCchar8* __glcMasterGetFaceName(const __GLCmaster* This,
|
||||
const __GLCcontext* inContext,
|
||||
const GLint inIndex)
|
||||
{
|
||||
FcFontSet *fontSet = NULL;
|
||||
FcPattern *pattern = NULL;
|
||||
GLCchar8* string = NULL;
|
||||
GLCchar8* faceName;
|
||||
GLint idx = inIndex;
|
||||
__GLCdata data = {NULL, NULL};
|
||||
int font = 0;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcResultMatch;
|
||||
#endif
|
||||
|
||||
data.master = This;
|
||||
data.index = &idx;
|
||||
|
||||
font = __glcScanFonts(inContext, &fontSet, &pattern, __glcFindMasterIndex,
|
||||
&data);
|
||||
|
||||
FcPatternDestroy(pattern);
|
||||
|
||||
if (font < 0) {
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (font == fontSet->nfont) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(fontSet->fonts[font], FC_STYLE, 0, &string);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(fontSet->fonts[font], FC_STYLE, 0, &string);
|
||||
#endif
|
||||
|
||||
#ifdef __WIN32__
|
||||
faceName = (GLCchar8*)_strdup((const char*)string);
|
||||
#else
|
||||
faceName = (GLCchar8*)strdup((const char*)string);
|
||||
#endif
|
||||
FcFontSetDestroy(fontSet);
|
||||
if (!faceName) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return faceName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Is this a fixed font ? */
|
||||
GLboolean __glcMasterIsFixedPitch(const __GLCmaster* This)
|
||||
{
|
||||
int fixed = 0;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcResultMatch;
|
||||
|
||||
result = FcPatternGetInteger(This->pattern, FC_SPACING, 0, &fixed);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetInteger(This->pattern, FC_SPACING, 0, &fixed);
|
||||
#endif
|
||||
return fixed ? GL_TRUE: GL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GLboolean __glcCountFaces(FcFontSet* GLC_UNUSED_ARG(inFontSet),
|
||||
FcPattern* inPattern,
|
||||
int GLC_UNUSED_ARG(inFont), void* inData)
|
||||
{
|
||||
const __GLCmaster* This = ((__GLCdata*)inData)->master;
|
||||
GLint *count = ((__GLCdata*)inData)->index;
|
||||
FcBool equal = FcPatternEqual(inPattern, This->pattern);
|
||||
|
||||
if (equal)
|
||||
(*count)++;
|
||||
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the face count of the master */
|
||||
GLint __glcMasterFaceCount(const __GLCmaster* This,
|
||||
const __GLCcontext* inContext)
|
||||
{
|
||||
FcFontSet *fontSet = NULL;
|
||||
GLint count = 0;
|
||||
FcPattern* pattern = NULL;
|
||||
__GLCdata data = {NULL, NULL};
|
||||
int font = 0;
|
||||
|
||||
data.master = This;
|
||||
data.index = &count;
|
||||
|
||||
font = __glcScanFonts(inContext, &fontSet, &pattern, __glcCountFaces, &data);
|
||||
|
||||
FcFontSetDestroy(fontSet);
|
||||
|
||||
if (font < 0)
|
||||
return 0;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This subroutine is called whenever the user wants to access to informations
|
||||
* that have not been loaded from the font files yet. In order to reduce disk
|
||||
* accesses, informations such as the master format, full name or version are
|
||||
* read "just in time" i.e. only when the user requests them.
|
||||
*/
|
||||
const GLCchar8* __glcMasterGetInfo(const __GLCmaster* This,
|
||||
__GLCcontext* inContext,
|
||||
const GLCenum inAttrib)
|
||||
{
|
||||
__GLCfaceDescriptor* faceDesc = NULL;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcResultMatch;
|
||||
#endif
|
||||
GLCchar8 *string = NULL;
|
||||
const GLCchar8* info = NULL;
|
||||
const GLCchar8 *buffer = NULL;
|
||||
|
||||
/* Get the Unicode string which corresponds to the requested attribute */
|
||||
switch(inAttrib) {
|
||||
case GLC_FAMILY:
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(This->pattern, FC_FAMILY, 0, &string);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(This->pattern, FC_FAMILY, 0, &string);
|
||||
#endif
|
||||
return string;
|
||||
case GLC_VENDOR:
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(This->pattern, FC_FOUNDRY, 0, &string);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(This->pattern, FC_FOUNDRY, 0, &string);
|
||||
#endif
|
||||
return string;
|
||||
case GLC_VERSION:
|
||||
case GLC_FULL_NAME_SGI:
|
||||
case GLC_MASTER_FORMAT:
|
||||
faceDesc = __glcFaceDescCreate(This, NULL, inContext, 0);
|
||||
|
||||
if (!faceDesc)
|
||||
return NULL;
|
||||
|
||||
#ifndef GLC_FT_CACHE
|
||||
if (!__glcFaceDescOpen(faceDesc, inContext)) {
|
||||
__glcFaceDescDestroy(faceDesc, inContext);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
info = __glcFaceDescGetFontFormat(faceDesc, inContext, inAttrib);
|
||||
if (info) {
|
||||
/* Convert the string and store it in the context buffer */
|
||||
buffer = (const GLCchar8*)__glcConvertFromUtf8ToBuffer(inContext, info);
|
||||
}
|
||||
else
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
|
||||
if (faceDesc) {
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFaceDescClose(faceDesc);
|
||||
#endif
|
||||
__glcFaceDescDestroy(faceDesc, inContext);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
/* The program is not supposed to reach the end of the function.
|
||||
* The following return is there to prevent the compiler to issue
|
||||
* a warning about 'control reaching the end of a non-void function'.
|
||||
*/
|
||||
return (const GLCchar8*)"";
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GLboolean __glcFindFamily(FcFontSet* inFontSet,
|
||||
FcPattern* GLC_UNUSED_ARG(inPattern),
|
||||
int inFont, void* inData)
|
||||
{
|
||||
FcChar8* family = NULL;
|
||||
#ifdef DEBUGMODE
|
||||
FcResult result = FcPatternGetString(inFontSet->fonts[inFont], FC_FAMILY, 0,
|
||||
&family);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(inFontSet->fonts[inFont], FC_FAMILY, 0, &family);
|
||||
#endif
|
||||
|
||||
if (strcmp((const char*)family, (const char*)inData))
|
||||
return GL_FALSE;
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a master on the basis of the family name */
|
||||
__GLCmaster* __glcMasterFromFamily(const __GLCcontext* inContext,
|
||||
const GLCchar8* inFamily)
|
||||
{
|
||||
FcFontSet *fontSet = NULL;
|
||||
FcPattern* pattern = NULL;
|
||||
__GLCmaster* This = NULL;
|
||||
int font = __glcScanFonts(inContext, &fontSet, &pattern, __glcFindFamily,
|
||||
(void*)inFamily);
|
||||
|
||||
if (font < 0) {
|
||||
if (pattern)
|
||||
FcPatternDestroy(pattern);
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(fontSet);
|
||||
|
||||
if (font == fontSet->nfont) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
if (pattern)
|
||||
FcPatternDestroy(pattern);
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(pattern);
|
||||
|
||||
This = (__GLCmaster*)__glcMalloc(sizeof(__GLCmaster));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
FcPatternDestroy(pattern);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCmaster));
|
||||
|
||||
This->pattern = pattern;
|
||||
FcFontSetDestroy(fontSet);
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a master which contains at least a font which math the character
|
||||
* identified by inCode.
|
||||
*/
|
||||
__GLCmaster* __glcMasterMatchCode(const __GLCcontext* inContext,
|
||||
const GLint inCode)
|
||||
{
|
||||
__GLCmaster* This = NULL;
|
||||
FcPattern* pattern = NULL;
|
||||
FcFontSet* fontSet = NULL;
|
||||
FcFontSet* fontSet2 = NULL;
|
||||
FcObjectSet* objectSet = NULL;
|
||||
FcResult result = FcResultMatch;
|
||||
int f = 0;
|
||||
FcChar8* family = NULL;
|
||||
int fixed = 0;
|
||||
FcChar8* foundry = NULL;
|
||||
FcCharSet* charSet = FcCharSetCreate();
|
||||
|
||||
if (!charSet)
|
||||
return NULL;
|
||||
|
||||
if (!FcCharSetAddChar(charSet, inCode)) {
|
||||
FcCharSetDestroy(charSet);
|
||||
return NULL;
|
||||
}
|
||||
pattern = FcPatternBuild(NULL, FC_CHARSET, FcTypeCharSet, charSet,
|
||||
FC_OUTLINE, FcTypeBool, FcTrue, NULL);
|
||||
FcCharSetDestroy(charSet);
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!FcConfigSubstitute(inContext->config, pattern, FcMatchPattern)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcPatternDestroy(pattern);
|
||||
return NULL;
|
||||
}
|
||||
FcDefaultSubstitute(pattern);
|
||||
fontSet = FcFontSort(inContext->config, pattern, FcFalse, NULL, &result);
|
||||
FcPatternDestroy(pattern);
|
||||
if ((!fontSet) || (result == FcResultTypeMismatch)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (f = 0; f < fontSet->nfont; f++) {
|
||||
FcBool outline = FcFalse;
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetBool(fontSet->fonts[f], FC_OUTLINE, 0, &outline);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetCharSet(fontSet->fonts[f], FC_CHARSET, 0, &charSet);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetBool(fontSet->fonts[f], FC_OUTLINE, 0, &outline);
|
||||
FcPatternGetCharSet(fontSet->fonts[f], FC_CHARSET, 0, &charSet);
|
||||
#endif
|
||||
|
||||
if (outline && FcCharSetHasChar(charSet, inCode))
|
||||
break;
|
||||
}
|
||||
|
||||
if (f == fontSet->nfont) {
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Ugly hack to extract a subset of the pattern fontSet->fonts[f]
|
||||
* (otherwise the hash value will not match any value of the hash table).
|
||||
*/
|
||||
objectSet = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, FC_OUTLINE, FC_SPACING,
|
||||
NULL);
|
||||
if (!objectSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
fontSet2 = FcFontList(inContext->config, fontSet->fonts[f], objectSet);
|
||||
FcObjectSetDestroy(objectSet);
|
||||
if (!fontSet2) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(fontSet2->nfont);
|
||||
|
||||
This = (__GLCmaster*)__glcMalloc(sizeof(__GLCmaster));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FcFontSetDestroy(fontSet);
|
||||
FcFontSetDestroy(fontSet2);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCmaster));
|
||||
|
||||
/* Duplicate the pattern of the found font (otherwise it will be deleted with
|
||||
* the font set).
|
||||
*/
|
||||
#ifdef DEBUGMODE
|
||||
result = FcPatternGetString(fontSet2->fonts[0], FC_FAMILY, 0, &family);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetString(fontSet2->fonts[0], FC_FOUNDRY, 0, &foundry);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
result = FcPatternGetInteger(fontSet2->fonts[0], FC_SPACING, 0, &fixed);
|
||||
assert(result != FcResultTypeMismatch);
|
||||
#else
|
||||
FcPatternGetString(fontSet2->fonts[0], FC_FAMILY, 0, &family);
|
||||
FcPatternGetString(fontSet2->fonts[0], FC_FOUNDRY, 0, &foundry);
|
||||
FcPatternGetInteger(fontSet2->fonts[0], FC_SPACING, 0, &fixed);
|
||||
#endif
|
||||
|
||||
if (foundry)
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family, FC_FOUNDRY,
|
||||
FcTypeString, foundry, FC_SPACING, FcTypeInteger,
|
||||
fixed, NULL);
|
||||
else
|
||||
pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family, FC_SPACING,
|
||||
FcTypeInteger, fixed, NULL);
|
||||
|
||||
FcFontSetDestroy(fontSet2);
|
||||
FcFontSetDestroy(fontSet);
|
||||
if (!pattern) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->pattern = pattern;
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GLint __glcMasterGetID(const __GLCmaster* This, const __GLCcontext* inContext)
|
||||
{
|
||||
GLCchar32 hashValue = GLC_MASTER_HASH_VALUE(This);
|
||||
GLint i = 0;
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(inContext->masterHashTable);
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(inContext->masterHashTable); i++) {
|
||||
if (hashValue == hashTable[i])
|
||||
break;
|
||||
}
|
||||
assert(i < GLC_ARRAY_LENGTH(inContext->masterHashTable));
|
||||
|
||||
return i;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the object __GLCmaster which manage the masters
|
||||
*/
|
||||
|
||||
#ifndef __glc_omaster_h
|
||||
#define __glc_omaster_h
|
||||
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
#include "ocharmap.h"
|
||||
|
||||
#define GLC_MASTER_HASH_VALUE(master) FcPatternHash((master)->pattern)
|
||||
|
||||
struct __GLCmasterRec {
|
||||
FcPattern* pattern;
|
||||
};
|
||||
|
||||
__GLCmaster* __glcMasterCreate(const GLint inMaster,
|
||||
const __GLCcontext* inContext);
|
||||
void __glcMasterDestroy(__GLCmaster* This);
|
||||
GLCchar8* __glcMasterGetFaceName(const __GLCmaster* This,
|
||||
const __GLCcontext* inContext,
|
||||
const GLint inIndex);
|
||||
GLboolean __glcMasterIsFixedPitch(const __GLCmaster* This);
|
||||
GLint __glcMasterFaceCount(const __GLCmaster* This,
|
||||
const __GLCcontext* inContext);
|
||||
const GLCchar8* __glcMasterGetInfo(const __GLCmaster* This,
|
||||
__GLCcontext* inContext,
|
||||
const GLCenum inAttrib);
|
||||
__GLCmaster* __glcMasterFromFamily(const __GLCcontext* inContext,
|
||||
const GLCchar8* inFamily);
|
||||
__GLCmaster* __glcMasterMatchCode(const __GLCcontext* inContext,
|
||||
const GLint inCode);
|
||||
GLint __glcMasterGetID(const __GLCmaster* This, const __GLCcontext* inContext);
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
/* Name of package */
|
||||
#define PACKAGE "quesoglc"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "quesoglc-general@lists.sourceforge.net"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "QuesoGLC"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "QuesoGLC custom"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "quesoglc"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "9999"
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION "9999"
|
||||
|
|
@ -0,0 +1,996 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the so-called "Rendering commands" described in chapter 3.9 of the
|
||||
* GLC specs.
|
||||
*/
|
||||
|
||||
/** \defgroup render Rendering commands
|
||||
* These are the commands that render characters to a GL render target. Those
|
||||
* commands gather glyph datas according to the parameters that has been set in
|
||||
* the state machine of GLC, and issue GL commands to render the characters
|
||||
* layout to the GL render target.
|
||||
*
|
||||
* When it renders a character, GLC finds a font that maps the character code
|
||||
* to a character such as LATIN CAPITAL LETTER A, then uses one or more glyphs
|
||||
* from the font to create a graphical layout that represents the character.
|
||||
* Finally, GLC issues a sequence of GL commands to draw the layout. Glyph
|
||||
* coordinates are defined in EM units and are transformed during rendering to
|
||||
* produce the desired mapping of the glyph shape into the GL window coordinate
|
||||
* system.
|
||||
*
|
||||
* If GLC cannot find a font that maps the character code in the list
|
||||
* \b GLC_CURRENT_FONT_LIST, it attemps to produce an alternate rendering. If
|
||||
* the value of the boolean variable \b GLC_AUTO_FONT is set to \b GL_TRUE, GLC
|
||||
* searches for a font that has the character that maps the character code. If
|
||||
* the search succeeds, the font's ID is appended to \b GLC_CURRENT_FONT_LIST
|
||||
* and the character is rendered.
|
||||
*
|
||||
* If there are fonts in the list \b GLC_CURRENT_FONT_LIST, but a match for
|
||||
* the character code cannot be found in any of those fonts, GLC goes through
|
||||
* the following steps :
|
||||
* -# If the value of the variable \b GLC_REPLACEMENT_CODE is nonzero,
|
||||
* GLC finds a font that maps the replacement code, and renders the character
|
||||
* that the replacement code is mapped to.
|
||||
* -# If the variable \b GLC_REPLACEMENT_CODE is zero, or if the replacement
|
||||
* code does not result in a match, GLC checks whether a callback function is
|
||||
* defined. If a callback function is defined for \b GLC_OP_glcUnmappedCode,
|
||||
* GLC calls the function. The callback function provides the character code to
|
||||
* the user and allows loading of the appropriate font. After the callback
|
||||
* returns, GLC tries to render the character code again.
|
||||
* -# Otherwise, the command attemps to render the character sequence
|
||||
* <em>\\\<hexcode\></em>, where \\ is the character REVERSE SOLIDUS (U+5C),
|
||||
* \< is the character LESS-THAN SIGN (U+3C), \> is the character GREATER-THAN
|
||||
* SIGN (U+3E), and \e hexcode is the character code represented as a sequence
|
||||
* of hexadecimal digits. The sequence has no leading zeros, and alphabetic
|
||||
* digits are in upper case. The GLC measurement commands treat the sequence
|
||||
* as a single character.
|
||||
*
|
||||
* \note The rendering commands may issue GL commands, hence a GL context must
|
||||
* be bound to the current thread such that the GLC commands produce the desired
|
||||
* result. It is the responsibility of the GLC client to set up the underlying
|
||||
* GL implementation.
|
||||
*
|
||||
* \note Some rendering commands create and/or use display lists and/or
|
||||
* textures. The IDs of those display lists and textures are stored in the
|
||||
* current GLC context but the display lists and the textures themselves are
|
||||
* managed by the current GL context. In order not to impact the performance of
|
||||
* error-free programs, QuesoGLC does not check if the current GL context is
|
||||
* the same than the one where the display lists and the textures were actually
|
||||
* created. If the current GL context has changed meanwhile, the result of
|
||||
* commands that refer to the corresponding display lists or textures is
|
||||
* undefined.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#if defined __APPLE__ && defined __MACH__
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
|
||||
/* This internal function renders a glyph using the GLC_BITMAP format */
|
||||
/* TODO : Render Bitmap fonts */
|
||||
static void __glcRenderCharBitmap(const __GLCfont* inFont,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat inScaleX,
|
||||
const GLfloat inScaleY,
|
||||
const GLfloat* inAdvance,
|
||||
const GLboolean inIsRTL)
|
||||
{
|
||||
GLfloat *transform = inContext->bitmapMatrix;
|
||||
GLint pixWidth = 0, pixHeight = 0;
|
||||
GLubyte* pixBuffer = NULL;
|
||||
GLint pixBoundingBox[4] = {0, 0, 0, 0};
|
||||
|
||||
__glcFontGetBitmapSize(inFont, &pixWidth, &pixHeight, inScaleX, inScaleY, 0,
|
||||
pixBoundingBox, inContext);
|
||||
|
||||
pixBuffer = (GLubyte *)__glcMalloc(pixWidth * pixHeight);
|
||||
if (!pixBuffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* render the glyph */
|
||||
if (!__glcFontGetBitmap(inFont, pixWidth, pixHeight, pixBuffer, inContext)) {
|
||||
__glcFree(pixBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do the actual GL rendering */
|
||||
if (inIsRTL) {
|
||||
glBitmap(0, 0, 0, 0,
|
||||
inAdvance[1] * transform[2] - inAdvance[0] * transform[0],
|
||||
inAdvance[1] * transform[3] - inAdvance[0] * transform[1],
|
||||
NULL);
|
||||
glBitmap(pixWidth, pixHeight, - pixBoundingBox[0] >> 6,
|
||||
-pixBoundingBox[1] >> 6, 0., 0., pixBuffer);
|
||||
}
|
||||
else
|
||||
glBitmap(pixWidth, pixHeight, -pixBoundingBox[0] >> 6,
|
||||
-pixBoundingBox[1] >> 6,
|
||||
inAdvance[0] * transform[0] + inAdvance[1] * transform[2],
|
||||
inAdvance[0] * transform[1] + inAdvance[1] * transform[3],
|
||||
pixBuffer);
|
||||
|
||||
__glcFree(pixBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This internal function renders a glyph using the GLC_PIXMAP_QSO format */
|
||||
static void __glcRenderCharPixmap(const __GLCfont* inFont,
|
||||
const __GLCcontext* inContext,
|
||||
const GLfloat scaleX, const GLfloat scaleY,
|
||||
const GLfloat* advance,
|
||||
const GLboolean inIsRTL)
|
||||
{
|
||||
GLfloat *transform = inContext->bitmapMatrix;
|
||||
GLint pixWidth = 0, pixHeight = 0;
|
||||
GLubyte* pixBuffer = NULL;
|
||||
GLint pixBoundingBox[4] = {0, 0, 0, 0};
|
||||
|
||||
__glcFontGetBitmapSize(inFont, &pixWidth, &pixHeight, scaleX, scaleY, 0,
|
||||
pixBoundingBox, inContext);
|
||||
|
||||
pixBuffer = (GLubyte *)__glcMalloc(pixWidth * pixHeight);
|
||||
if (!pixBuffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* render the glyph */
|
||||
if (!__glcFontGetBitmap(inFont, pixWidth, pixHeight, pixBuffer, inContext)) {
|
||||
__glcFree(pixBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do the actual GL rendering */
|
||||
if (inIsRTL) {
|
||||
glBitmap(0, 0, 0.f, 0.f,
|
||||
advance[1] * transform[2] - advance[0] * transform[0] +
|
||||
(pixBoundingBox[0] >> 6),
|
||||
advance[1] * transform[3] - advance[0] * transform[1] +
|
||||
(pixBoundingBox[1] >> 6),
|
||||
NULL);
|
||||
|
||||
glDrawPixels(pixWidth, pixHeight, GL_ALPHA, GL_UNSIGNED_BYTE, pixBuffer);
|
||||
|
||||
glBitmap(0, 0, 0.f, 0.f,
|
||||
-(pixBoundingBox[0] >> 6),
|
||||
-(pixBoundingBox[1] >> 6),
|
||||
NULL);
|
||||
}
|
||||
else {
|
||||
glBitmap(0, 0, 0.f, 0.f,
|
||||
pixBoundingBox[0] >> 6,
|
||||
pixBoundingBox[1] >> 6,
|
||||
NULL);
|
||||
|
||||
glDrawPixels(pixWidth, pixHeight, GL_ALPHA, GL_UNSIGNED_BYTE, pixBuffer);
|
||||
|
||||
glBitmap(0, 0, 0.f, 0.f,
|
||||
advance[0] * transform[0] + advance[1] * transform[2] -
|
||||
(pixBoundingBox[0] >> 6),
|
||||
advance[0] * transform[1] + advance[1] * transform[3] -
|
||||
(pixBoundingBox[1] >> 6),
|
||||
NULL);
|
||||
}
|
||||
|
||||
__glcFree(pixBuffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Internal function that is called to do the actual rendering :
|
||||
* 'inCode' must be given in UCS-4 format
|
||||
*/
|
||||
static void* __glcRenderChar(const GLint inCode, const GLint inPrevCode,
|
||||
const GLboolean inIsRTL, const __GLCfont* inFont,
|
||||
__GLCcontext* inContext,
|
||||
const void* GLC_UNUSED_ARG(inData),
|
||||
const GLboolean GLC_UNUSED_ARG(inMultipleChars))
|
||||
{
|
||||
GLfloat transformMatrix[16];
|
||||
GLfloat scaleX = GLC_POINT_SIZE;
|
||||
GLfloat scaleY = GLC_POINT_SIZE;
|
||||
__GLCglyph* glyph = NULL;
|
||||
GLfloat sx64 = 0., sy64 = 0.;
|
||||
GLfloat advance[2] = {0., 0.};
|
||||
|
||||
assert(inFont);
|
||||
|
||||
__glcGetScale(inContext, transformMatrix, &scaleX, &scaleY);
|
||||
|
||||
if ((fabs(scaleX) < GLC_EPSILON) || (fabs(scaleY) < GLC_EPSILON))
|
||||
return NULL;
|
||||
|
||||
#ifndef GLC_FT_CACHE
|
||||
if (!__glcFontOpen(inFont, inContext))
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
if (inPrevCode && inContext->enableState.kerning) {
|
||||
GLfloat kerning[2];
|
||||
GLint leftCode = inIsRTL ? inCode : inPrevCode;
|
||||
GLint rightCode = inIsRTL ? inPrevCode : inCode;
|
||||
|
||||
if (__glcFontGetKerning(inFont, leftCode, rightCode, kerning, inContext,
|
||||
scaleX, scaleY)) {
|
||||
if (inIsRTL)
|
||||
kerning[0] = -kerning[0];
|
||||
|
||||
if ((inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO))
|
||||
glBitmap(0, 0, 0, 0,
|
||||
kerning[0] * inContext->bitmapMatrix[0]
|
||||
+ kerning[1] * inContext->bitmapMatrix[2],
|
||||
kerning[0] * inContext->bitmapMatrix[1]
|
||||
+ kerning[1] * inContext->bitmapMatrix[3],
|
||||
NULL);
|
||||
else
|
||||
glTranslatef(kerning[0], kerning[1], 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!__glcFontGetAdvance(inFont, inCode, advance, inContext, scaleX,
|
||||
scaleY)) {
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFontClose(inFont);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get and load the glyph which unicode code is identified by inCode */
|
||||
glyph = __glcFontGetGlyph(inFont, inCode, inContext);
|
||||
|
||||
if (inContext->enableState.glObjects
|
||||
&& !__glcFontPrepareGlyph(inFont, inContext, scaleX, scaleY,
|
||||
glyph->index)) {
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFontClose(inFont);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sx64 = 64. * scaleX;
|
||||
sy64 = 64. * scaleY;
|
||||
|
||||
if ((inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)) {
|
||||
if (inIsRTL)
|
||||
glTranslatef(-advance[0], advance[1], 0.f);
|
||||
|
||||
/* If the outline contains no point then the glyph represents a space
|
||||
* character and there is no need to continue the process of rendering.
|
||||
*/
|
||||
if (!__glcFontOutlineEmpty(inFont)) {
|
||||
/* Update the advance and return */
|
||||
if (!inIsRTL)
|
||||
glTranslatef(advance[0], advance[1], 0.f);
|
||||
if (inContext->enableState.glObjects)
|
||||
glyph->isSpacingChar = GL_TRUE;
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFontClose(inFont);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* coordinates are given in 26.6 fixed point integer hence we
|
||||
* divide the scale by 2^6
|
||||
*/
|
||||
if (!inContext->enableState.glObjects)
|
||||
glScalef(1. / sx64, 1. / sy64, 1.f);
|
||||
}
|
||||
|
||||
/* Call the appropriate function depending on the rendering mode */
|
||||
switch(inContext->renderState.renderStyle) {
|
||||
case GLC_BITMAP:
|
||||
__glcRenderCharBitmap(inFont, inContext, scaleX, scaleY, advance,
|
||||
inIsRTL);
|
||||
break;
|
||||
case GLC_PIXMAP_QSO:
|
||||
__glcRenderCharPixmap(inFont, inContext, scaleX, scaleY, advance,
|
||||
inIsRTL);
|
||||
break;
|
||||
case GLC_TEXTURE:
|
||||
__glcRenderCharTexture(inFont, inContext, scaleX, scaleY, glyph);
|
||||
break;
|
||||
case GLC_LINE:
|
||||
__glcRenderCharScalable(inFont, inContext, transformMatrix, scaleX,
|
||||
scaleY, glyph);
|
||||
break;
|
||||
case GLC_TRIANGLE:
|
||||
__glcRenderCharScalable(inFont, inContext, transformMatrix, scaleX,
|
||||
scaleY, glyph);
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
}
|
||||
|
||||
if ((inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)) {
|
||||
if (!inContext->enableState.glObjects)
|
||||
glScalef(sx64, sy64, 1.);
|
||||
if (!inIsRTL)
|
||||
glTranslatef(advance[0], advance[1], 0.f);
|
||||
}
|
||||
#ifndef GLC_FT_CACHE
|
||||
__glcFontClose(inFont);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This internal function is used by both glcRenderString() and
|
||||
* glcRenderCountedString(). The string 'inString' must be sorted in visual
|
||||
* order and stored using UCS4 format.
|
||||
*/
|
||||
static void __glcRenderCountedString(__GLCcontext* inContext,
|
||||
const GLCchar32* inString,
|
||||
const GLboolean inIsRightToLeft,
|
||||
const GLint inCount)
|
||||
{
|
||||
GLint listIndex = 0;
|
||||
GLint i = 0;
|
||||
const GLCchar32* ptr = NULL;
|
||||
__GLCglState GLState;
|
||||
__GLCcharacter prevCode = {0, NULL, NULL, {0.f, 0.f}};
|
||||
GLboolean saveGLObjects = GL_FALSE;
|
||||
GLint shift = 1;
|
||||
__GLCcharacter* chars = NULL;
|
||||
GLfloat pixmapColor[4];
|
||||
|
||||
/* Disable the internal management of GL objects when the user is currently
|
||||
* building a display list.
|
||||
*/
|
||||
glGetIntegerv(GL_LIST_INDEX, &listIndex);
|
||||
if (listIndex) {
|
||||
saveGLObjects = inContext->enableState.glObjects;
|
||||
inContext->enableState.glObjects = GL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* Allocate a buffer to store the glyphes informations of the string to be
|
||||
* rendered.
|
||||
*/
|
||||
if (inContext->enableState.glObjects
|
||||
&& (inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)) {
|
||||
chars = (__GLCcharacter*)__glcMalloc(inCount * sizeof(__GLCcharacter));
|
||||
if (!chars) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the value of the GL parameters */
|
||||
__glcSaveGLState(&GLState, inContext, GL_FALSE);
|
||||
|
||||
/* Set the vertex arrays parameters for GLC_LINE and GLC_TRIANGLE rendering
|
||||
* styles when GLC_GL_OBJECTS is enabled.
|
||||
*/
|
||||
if (inContext->renderState.renderStyle == GLC_LINE ||
|
||||
(inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
&& !(inContext->enableState.glObjects
|
||||
&& inContext->enableState.extrude))) {
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_INDEX_ARRAY);
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisableClientState(GL_EDGE_FLAG_ARRAY);
|
||||
}
|
||||
|
||||
if (inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
&& inContext->enableState.glObjects && inContext->enableState.extrude)
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
/* Set the texture environment if the render style is GLC_TEXTURE */
|
||||
if (inContext->renderState.renderStyle == GLC_TEXTURE) {
|
||||
/* Set the new values of the parameters */
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
if (inContext->enableState.glObjects) {
|
||||
if (inContext->atlas.id)
|
||||
glBindTexture(GL_TEXTURE_2D, inContext->atlas.id);
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
if (inContext->atlas.bufferObjectID) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, inContext->atlas.bufferObjectID);
|
||||
glInterleavedArrays(GL_T2F_V3F, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (inContext->texture.id) {
|
||||
glBindTexture(GL_TEXTURE_2D, inContext->texture.id);
|
||||
if (GLEW_ARB_pixel_buffer_object && inContext->texture.bufferObjectID)
|
||||
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER,
|
||||
inContext->texture.bufferObjectID);
|
||||
}
|
||||
}
|
||||
|
||||
if ((inContext->renderState.renderStyle == GLC_BITMAP)
|
||||
|| (inContext->renderState.renderStyle == GLC_PIXMAP_QSO)) {
|
||||
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
if (inContext->renderState.renderStyle == GLC_PIXMAP_QSO) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glGetFloatv(GL_CURRENT_RASTER_COLOR, pixmapColor);
|
||||
glPixelTransferf(GL_RED_BIAS, pixmapColor[0]);
|
||||
glPixelTransferf(GL_GREEN_BIAS, pixmapColor[1]);
|
||||
glPixelTransferf(GL_BLUE_BIAS, pixmapColor[2]);
|
||||
glPixelTransferf(GL_ALPHA_BIAS, 0.f);
|
||||
glPixelTransferf(GL_RED_SCALE, 1.f);
|
||||
glPixelTransferf(GL_GREEN_SCALE, 1.f);
|
||||
glPixelTransferf(GL_BLUE_SCALE, 1.f);
|
||||
glPixelTransferf(GL_ALPHA_SCALE, pixmapColor[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Render the string */
|
||||
ptr = inString;
|
||||
if (inIsRightToLeft) {
|
||||
ptr += inCount - 1;
|
||||
shift = -1;
|
||||
}
|
||||
|
||||
if (inContext->enableState.glObjects
|
||||
&& (inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)) {
|
||||
__GLCfont* font = NULL;
|
||||
__GLCglyph* glyph = NULL;
|
||||
int length = 0;
|
||||
int j = 0;
|
||||
GLuint GLObjectIndex = inContext->renderState.renderStyle - 0x101;
|
||||
FT_ListNode node = NULL;
|
||||
float resolution = inContext->renderState.resolution / 72.;
|
||||
GLfloat orientation = 1.f;
|
||||
|
||||
if (inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
&& inContext->enableState.extrude) {
|
||||
GLfloat transformMatrix[16];
|
||||
GLfloat scaleX = GLC_POINT_SIZE;
|
||||
GLfloat scaleY = GLC_POINT_SIZE;
|
||||
|
||||
__glcGetScale(inContext, transformMatrix, &scaleX, &scaleY);
|
||||
|
||||
if ((fabs(scaleX) < GLC_EPSILON) || (fabs(scaleY) < GLC_EPSILON))
|
||||
return;
|
||||
|
||||
orientation = -transformMatrix[11];
|
||||
GLObjectIndex++;
|
||||
}
|
||||
|
||||
glNormal3f(0.f, 0.f, 1.f / resolution);
|
||||
|
||||
for (i = 0; i < inCount; i++) {
|
||||
if (*ptr >= 32) {
|
||||
for (node = inContext->currentFontList.head; node ; node = node->next) {
|
||||
font = (__GLCfont*)node->data;
|
||||
glyph = __glcCharMapGetGlyph(font->charMap, *ptr);
|
||||
|
||||
if (glyph) {
|
||||
if (!glyph->glObject[GLObjectIndex] && !glyph->isSpacingChar)
|
||||
continue;
|
||||
|
||||
if (!glyph->isSpacingChar
|
||||
&& (inContext->renderState.renderStyle == GLC_TEXTURE))
|
||||
FT_List_Up(&inContext->atlasList,
|
||||
(FT_ListNode)glyph->textureObject);
|
||||
|
||||
chars[length].glyph = glyph;
|
||||
chars[length].advance[0] = glyph->advance[0];
|
||||
chars[length].advance[1] = glyph->advance[1];
|
||||
|
||||
if (inContext->enableState.kerning) {
|
||||
if (prevCode.code && prevCode.font == font) {
|
||||
GLfloat kerning[2];
|
||||
GLint leftCode = inIsRightToLeft ? *ptr : prevCode.code;
|
||||
GLint rightCode = inIsRightToLeft ? prevCode.code : *ptr;
|
||||
|
||||
if (__glcFontGetKerning(font, leftCode, rightCode, kerning,
|
||||
inContext, GLC_POINT_SIZE,
|
||||
GLC_POINT_SIZE)) {
|
||||
if (inIsRightToLeft)
|
||||
kerning[0] = -kerning[0];
|
||||
|
||||
if (length) {
|
||||
chars[length - 1].advance[0] += kerning[0];
|
||||
chars[length - 1].advance[1] += kerning[1];
|
||||
}
|
||||
else
|
||||
glTranslatef(kerning[0], kerning[1], 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevCode.font = font;
|
||||
prevCode.code = *ptr;
|
||||
|
||||
if (glyph->isSpacingChar)
|
||||
chars[length].code = 32;
|
||||
else
|
||||
chars[length].code = *ptr;
|
||||
|
||||
length++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!node || (i == inCount-1)) {
|
||||
glScalef(resolution, resolution, 1.f);
|
||||
|
||||
for (j = 0; j < length; j++) {
|
||||
if (inIsRightToLeft)
|
||||
glTranslatef(-chars[j].advance[0], chars[j].advance[1], 0.);
|
||||
if (chars[j].code != 32) {
|
||||
glyph = chars[j].glyph;
|
||||
|
||||
switch(inContext->renderState.renderStyle) {
|
||||
case GLC_TEXTURE:
|
||||
if (GLEW_ARB_vertex_buffer_object)
|
||||
glDrawArrays(GL_QUADS, glyph->textureObject->position * 4, 4);
|
||||
else
|
||||
glCallList(glyph->glObject[1]);
|
||||
break;
|
||||
case GLC_LINE:
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
int k = 0;
|
||||
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, glyph->glObject[0]);
|
||||
glVertexPointer(2, GL_FLOAT, 0, NULL);
|
||||
for (k = 0; k < glyph->nContour; k++)
|
||||
glDrawArrays(GL_LINE_LOOP, glyph->contours[k],
|
||||
glyph->contours[k+1] - glyph->contours[k]);
|
||||
break;
|
||||
}
|
||||
glCallList(glyph->glObject[0]);
|
||||
break;
|
||||
case GLC_TRIANGLE:
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
int k = 0;
|
||||
GLboolean extrude = GL_FALSE;
|
||||
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, glyph->glObject[0]);
|
||||
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
|
||||
glyph->glObject[2]);
|
||||
glVertexPointer(2, GL_FLOAT, 0, NULL);
|
||||
|
||||
do {
|
||||
GLuint* vertexIndices = NULL;
|
||||
|
||||
if (orientation > 0.f) {
|
||||
for (k = 0; k < glyph->nGeomBatch; k++) {
|
||||
glDrawRangeElements(glyph->geomBatches[k].mode,
|
||||
glyph->geomBatches[k].start,
|
||||
glyph->geomBatches[k].end,
|
||||
glyph->geomBatches[k].length,
|
||||
GL_UNSIGNED_INT, vertexIndices);
|
||||
vertexIndices += glyph->geomBatches[k].length;
|
||||
}
|
||||
}
|
||||
|
||||
if (inContext->enableState.extrude) {
|
||||
if (extrude) {
|
||||
glTranslatef(0.f, 0.f, 1.f);
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB,
|
||||
glyph->glObject[3]);
|
||||
glInterleavedArrays(GL_N3F_V3F, 0, NULL);
|
||||
|
||||
for (k = 0; k < glyph->nContour; k++)
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, glyph->contours[k] * 2,
|
||||
(glyph->contours[k+1] - glyph->contours[k]
|
||||
+ 1) * 2);
|
||||
glNormal3f(0.f, 0.f, 1.f / resolution);
|
||||
}
|
||||
else {
|
||||
glNormal3f(0.f, 0.f, -1.f / resolution);
|
||||
glTranslatef(0.f, 0.f, -1.f);
|
||||
orientation = -orientation;
|
||||
}
|
||||
extrude = (!extrude);
|
||||
}
|
||||
} while(extrude);
|
||||
}
|
||||
else
|
||||
glCallList(glyph->glObject[GLObjectIndex]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inIsRightToLeft)
|
||||
glTranslatef(chars[j].advance[0], chars[j].advance[1], 0.);
|
||||
}
|
||||
|
||||
if (!node)
|
||||
__glcProcessChar(inContext, *ptr, &prevCode, inIsRightToLeft,
|
||||
__glcRenderChar, NULL);
|
||||
|
||||
glScalef(1./resolution, 1./resolution, 1.f);
|
||||
length = 0;
|
||||
}
|
||||
|
||||
ptr += shift;
|
||||
}
|
||||
}
|
||||
else {
|
||||
glNormal3f(0.f, 0.f, 1.f);
|
||||
|
||||
for (i = 0; i < inCount; i++) {
|
||||
if (*ptr >= 32)
|
||||
__glcProcessChar(inContext, *ptr, &prevCode, inIsRightToLeft,
|
||||
__glcRenderChar, NULL);
|
||||
ptr += shift;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore the values of the GL state if needed */
|
||||
__glcRestoreGLState(&GLState, inContext, GL_FALSE);
|
||||
|
||||
if ((inContext->renderState.renderStyle != GLC_BITMAP)
|
||||
&& (inContext->renderState.renderStyle != GLC_PIXMAP_QSO)
|
||||
&& inContext->enableState.glObjects)
|
||||
__glcFree(chars);
|
||||
|
||||
if (listIndex)
|
||||
inContext->enableState.glObjects = saveGLObjects;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command renders the character that \e inCode is mapped to.
|
||||
* \param inCode The character to render
|
||||
* \sa glcRenderString()
|
||||
* \sa glcRenderCountedString()
|
||||
* \sa glcReplacementCode()
|
||||
* \sa glcRenderStyle()
|
||||
* \sa glcCallbackFunc()
|
||||
*/
|
||||
void APIENTRY glcRenderChar(GLint inCode)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLint code = 0;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the character code converted to the UCS-4 format */
|
||||
code = __glcConvertGLintToUcs4(ctx, inCode);
|
||||
if (code < 32)
|
||||
return; /* Skip control characters and unknown characters */
|
||||
|
||||
__glcRenderCountedString(ctx, (GLCchar32*)&code, GL_FALSE, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command is identical to the command glcRenderChar(), except that it
|
||||
* renders a string of characters. The string comprises the first \e inCount
|
||||
* elements of the array \e inString, which need not be followed by a zero
|
||||
* element.
|
||||
*
|
||||
* The command raises \b GLC_PARAMETER_ERROR if \e inCount is less than zero.
|
||||
* \param inCount The number of elements in the string to be rendered
|
||||
* \param inString The array of characters from which to render \e inCount
|
||||
* elements.
|
||||
* \sa glcRenderChar()
|
||||
* \sa glcRenderString()
|
||||
*/
|
||||
void APIENTRY glcRenderCountedString(GLint inCount, const GLCchar *inString)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLCchar32* UinString = NULL;
|
||||
GLboolean isRightToLeft = GL_FALSE;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if inCount is positive */
|
||||
if (inCount < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If inString is NULL then there is no point in continuing */
|
||||
if (!inString)
|
||||
return;
|
||||
|
||||
/* Creates a Unicode string based on the current string type. Basically,
|
||||
* that means that inString is read in the current string format.
|
||||
*/
|
||||
UinString = __glcConvertCountedStringToVisualUcs4(ctx, &isRightToLeft,
|
||||
inString, inCount);
|
||||
if (!UinString)
|
||||
return;
|
||||
|
||||
|
||||
__glcRenderCountedString(ctx, UinString, isRightToLeft, inCount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command is identical to the command glcRenderCountedString(), except
|
||||
* that \e inString is zero terminated, not counted.
|
||||
* \param inString A zero-terminated string of characters.
|
||||
* \sa glcRenderChar()
|
||||
* \sa glcRenderCountedString()
|
||||
*/
|
||||
void APIENTRY glcRenderString(const GLCchar *inString)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLCchar32* UinString = NULL;
|
||||
GLboolean isRightToLeft = GL_FALSE;
|
||||
GLint length = 0;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If inString is NULL then there is no point in continuing */
|
||||
if (!inString)
|
||||
return;
|
||||
|
||||
/* Creates a Unicode string based on the current string type. Basically,
|
||||
* that means that inString is read in the current string format.
|
||||
*/
|
||||
UinString = __glcConvertToVisualUcs4(ctx, &isRightToLeft, &length, inString);
|
||||
if (!UinString)
|
||||
return;
|
||||
|
||||
__glcRenderCountedString(ctx, UinString, isRightToLeft, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command assigns the value \e inStyle to the variable
|
||||
* \b GLC_RENDER_STYLE. Legal values for \e inStyle are defined in the table
|
||||
* below :
|
||||
* <center>
|
||||
* <table>
|
||||
* <caption>Rendering styles</caption>
|
||||
* <tr>
|
||||
* <td>Name</td> <td>Enumerant</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_BITMAP</b></td> <td>0x0100</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_LINE</b></td> <td>0x0101</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_TEXTURE</b></td> <td>0x0102</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_TRIANGLE</b></td> <td>0x0103</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td><b>GLC_PIXMAP_QSO</b></td> <td>0x8011</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* </center>
|
||||
* \param inStyle The value to assign to the variable \b GLC_RENDER_STYLE.
|
||||
* \sa glcGeti() with argument \b GLC_RENDER_STYLE
|
||||
*/
|
||||
void APIENTRY glcRenderStyle(GLCenum inStyle)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if inStyle has a legal value */
|
||||
switch(inStyle) {
|
||||
case GLC_BITMAP:
|
||||
case GLC_LINE:
|
||||
case GLC_TEXTURE:
|
||||
case GLC_TRIANGLE:
|
||||
case GLC_PIXMAP_QSO:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the current thread owns a current state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stores the rendering style */
|
||||
ctx->renderState.renderStyle = inStyle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command assigns the value \e inCode to the variable
|
||||
* \b GLC_REPLACEMENT_CODE. The replacement code is the code which is used
|
||||
* whenever glcRenderChar() can not find a font that owns a character which
|
||||
* the parameter \e inCode of glcRenderChar() maps to.
|
||||
* \param inCode An integer to assign to \b GLC_REPLACEMENT_CODE.
|
||||
* \sa glcGeti() with argument \b GLC_REPLACEMENT_CODE
|
||||
* \sa glcRenderChar()
|
||||
*/
|
||||
void APIENTRY glcReplacementCode(GLint inCode)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLint code = 0;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if the current thread owns a current state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the replacement character converted to the UCS-4 format */
|
||||
code = __glcConvertGLintToUcs4(ctx, inCode);
|
||||
if (code < 0)
|
||||
return;
|
||||
|
||||
/* Stores the replacement code */
|
||||
ctx->stringState.replacementCode = code;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command assigns the value \e inVal to the variable \b GLC_RESOLUTION.
|
||||
* It is used to compute the size of characters in pixels from the size in
|
||||
* points.
|
||||
*
|
||||
* The resolution is given in \e dpi (dots per inch). If \e inVal is zero, the
|
||||
* resolution defaults to 72 dpi.
|
||||
*
|
||||
* The command raises \b GLC_PARAMETER_ERROR if \e inVal is negative.
|
||||
* \param inVal A floating point number to be used as resolution.
|
||||
* \sa glcGetf() with argument GLC_RESOLUTION
|
||||
*/
|
||||
void APIENTRY glcResolution(GLfloat inVal)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Negative resolutions are illegal */
|
||||
if (inVal < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the current thread owns a current state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stores the resolution */
|
||||
ctx->renderState.resolution = (inVal < GLC_EPSILON) ? 72. : inVal;
|
||||
|
||||
/* Force the measurement caches to be updated */
|
||||
for (node = ctx->fontList.head; node; node = node->next) {
|
||||
__GLCfont* font = (__GLCfont*)node->data;
|
||||
__GLCfaceDescriptor* faceDesc = font->faceDesc;
|
||||
FT_ListNode glyphNode = NULL;
|
||||
|
||||
font->maxMetricCached = GL_FALSE;
|
||||
|
||||
for (glyphNode = faceDesc->glyphList.head; glyphNode;
|
||||
glyphNode = glyphNode->next) {
|
||||
__GLCglyph* glyph = (__GLCglyph*)glyphNode->data;
|
||||
|
||||
glyph->advanceCached = GL_FALSE;
|
||||
glyph->boundingBoxCached = GL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup render
|
||||
* This command assigns the value \b inVal to the floating point variable
|
||||
* identified by \e inAttrib which must be chosen in the table below.
|
||||
*
|
||||
* - \b GLC_PARAMETRIC_TOLERANCE_QSO specifies the maximum distance, in object
|
||||
* space, between the tesselation line contours and the curves they
|
||||
* approximate. This parameter is only relevant for the \b GLC_LINE and
|
||||
* \b GLC_TRIANGLE rendering types.
|
||||
*
|
||||
* \param inAttrib A symbolic constant indicating a GLC attribute.
|
||||
* \param inValue A floating point number to be used as tolerance.
|
||||
* \sa glcGetf() with argument GLC_PARAMETRIC_TOLERANCE_QSO
|
||||
*/
|
||||
void APIENTRY glcRenderParameterfQSO(GLenum inAttrib, GLfloat inVal)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if inAttrib has a legal value */
|
||||
switch(inAttrib) {
|
||||
case GLC_PARAMETRIC_TOLERANCE_QSO:
|
||||
break;
|
||||
default:
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inVal <= 0.f) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the current thread owns a current state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stores the tolerance */
|
||||
ctx->renderState.tolerance = inVal;
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,921 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the routines used to render characters with lines and triangles.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#if defined __APPLE__ && defined __MACH__
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#define GLC_MAX_ITER 50
|
||||
|
||||
|
||||
|
||||
/* Transform the object coordinates in the array 'inCoord' in screen
|
||||
* coordinates. The function updates 'inCoord' according to :
|
||||
* inCoord[0..1] contains the 2D glyph coordinates in the object space
|
||||
* inCoord[2..4] contains the 2D homogeneous coordinates in observer space
|
||||
*/
|
||||
static void __glcComputePixelCoordinates(GLfloat* inCoord,
|
||||
const __GLCrendererData* inData)
|
||||
{
|
||||
GLfloat x = inCoord[0] * inData->transformMatrix[0]
|
||||
+ inCoord[1] * inData->transformMatrix[4]
|
||||
+ inData->transformMatrix[12];
|
||||
GLfloat y = inCoord[0] * inData->transformMatrix[1]
|
||||
+ inCoord[1] * inData->transformMatrix[5]
|
||||
+ inData->transformMatrix[13];
|
||||
GLfloat w = inCoord[0] * inData->transformMatrix[3]
|
||||
+ inCoord[1] * inData->transformMatrix[7]
|
||||
+ inData->transformMatrix[15];
|
||||
GLfloat norm = x * x + y * y;
|
||||
|
||||
/* If w is very small compared to x, y and z this probably means that the
|
||||
* transformation matrix is ill-conditioned (i.e. its determinant is
|
||||
* numerically null)
|
||||
*/
|
||||
if (w * w < norm * GLC_EPSILON * GLC_EPSILON) {
|
||||
/* Ugly hack to handle the singularity of w */
|
||||
w = sqrt(norm) * GLC_EPSILON;
|
||||
}
|
||||
|
||||
inCoord[2] = x;
|
||||
inCoord[3] = y;
|
||||
inCoord[4] = w;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* __glcdeCasteljauConic :
|
||||
* renders conic Bezier curves using the de Casteljau subdivision algorithm
|
||||
*
|
||||
* This function creates a piecewise linear curve which is close enough
|
||||
* to the real Bezier curve. The piecewise linear curve is built so that
|
||||
* the chordal distance is lower than a tolerance value.
|
||||
* The chordal distance is taken to be the perpendicular distance from each
|
||||
* control point to the chord. This may not always be correct, but, in the small
|
||||
* lengths which are being considered, this is good enough.
|
||||
* A second simplifying assumption is that when too large a chordal distance is
|
||||
* encountered, the chord is split at the parametric midpoint, rather than
|
||||
* guessing the exact location of the best chord. This could lead to slightly
|
||||
* sub-optimal lines, but it provides a fast method for choosing the
|
||||
* subdivision point. This guess can be refined by lengthening the lines.
|
||||
*/
|
||||
int __glcdeCasteljauConic(void *inUserData)
|
||||
{
|
||||
__GLCrendererData *data = (__GLCrendererData *) inUserData;
|
||||
GLfloat* vector = data->vector;
|
||||
GLfloat(*controlPoint)[5] = NULL;
|
||||
GLint nArc = 1, arc = 0, rank = 0;
|
||||
int iter = 0;
|
||||
GLfloat* cp = (GLfloat*)__glcArrayInsertCell(data->controlPoints,
|
||||
GLC_ARRAY_LENGTH(data->controlPoints), 3);
|
||||
|
||||
if (!cp) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Append the control points to the vertex array */
|
||||
memcpy(cp, vector, 2 * sizeof(GLfloat));
|
||||
__glcComputePixelCoordinates(cp, data);
|
||||
|
||||
/* Append the first vertex of the curve to the vertex array */
|
||||
rank = GLC_ARRAY_LENGTH(data->vertexArray);
|
||||
if (!__glcArrayAppend(data->vertexArray, cp)) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Build the array of the control points */
|
||||
for (iter = 0; iter < 2; iter++) {
|
||||
cp += 5;
|
||||
vector += 2;
|
||||
memcpy(cp, vector, 2 * sizeof(GLfloat));
|
||||
__glcComputePixelCoordinates(cp, data);
|
||||
}
|
||||
|
||||
/* controlPoint[] must be computed there because
|
||||
* GLC_ARRAY_DATA(data->controlPoints) may have been modified by a realloc()
|
||||
* in __glcArrayInsert().
|
||||
*/
|
||||
controlPoint = (GLfloat(*)[5])GLC_ARRAY_DATA(data->controlPoints);
|
||||
|
||||
/* Here the de Casteljau algorithm begins */
|
||||
for (iter = 0; (iter < GLC_MAX_ITER) && (arc != nArc); iter++) {
|
||||
GLfloat ax = controlPoint[0][2];
|
||||
GLfloat ay = controlPoint[0][3];
|
||||
GLfloat aw = controlPoint[0][4];
|
||||
GLfloat abx = controlPoint[2][2]*aw - ax*controlPoint[2][4];
|
||||
GLfloat aby = controlPoint[2][3]*aw - ay*controlPoint[2][4];
|
||||
/* For the middle control point, compute its chordal distance that is its
|
||||
* distance from the segment AB
|
||||
*/
|
||||
GLfloat mw = controlPoint[1][4];
|
||||
GLfloat s = ((controlPoint[1][2]*aw - ax*mw) * aby
|
||||
- (controlPoint[1][3]*aw - ay*mw) * abx)
|
||||
/ (aw * mw);
|
||||
GLfloat dmax = s * s;
|
||||
|
||||
|
||||
if (dmax < data->tolerance * (abx * abx + aby *aby)) {
|
||||
arc++; /* Process the next arc */
|
||||
controlPoint = ((GLfloat(*)[5])GLC_ARRAY_DATA(data->controlPoints))+2*arc;
|
||||
/* Update the place where new vertices will be inserted in the vertex
|
||||
* array
|
||||
*/
|
||||
rank++;
|
||||
}
|
||||
else {
|
||||
/* Split an arc into two smaller arcs (this is the actual de Casteljau
|
||||
* algorithm)
|
||||
*/
|
||||
GLfloat *p1, *p2;
|
||||
GLfloat *pm = (GLfloat*)__glcArrayInsertCell(data->controlPoints,
|
||||
2*arc+1, 2);
|
||||
|
||||
if (!pm) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* controlPoint[] must be updated there because
|
||||
* data->controlPoints->data may have been modified by a realloc() in
|
||||
* __glcArrayInsert()
|
||||
*/
|
||||
controlPoint = ((GLfloat(*)[5])GLC_ARRAY_DATA(data->controlPoints))+2*arc;
|
||||
|
||||
p1 = controlPoint[0];
|
||||
p2 = controlPoint[3];
|
||||
pm = controlPoint[1];
|
||||
|
||||
pm[0] = 0.5*(p1[0]+p2[0]);
|
||||
pm[1] = 0.5*(p1[1]+p2[1]);
|
||||
pm[2] = 0.5*(p1[2]+p2[2]);
|
||||
pm[3] = 0.5*(p1[3]+p2[3]);
|
||||
pm[4] = 0.5*(p1[4]+p2[4]);
|
||||
|
||||
p1 = controlPoint[3];
|
||||
p2 = controlPoint[4];
|
||||
pm = controlPoint[3];
|
||||
|
||||
pm[0] = 0.5*(p1[0]+p2[0]);
|
||||
pm[1] = 0.5*(p1[1]+p2[1]);
|
||||
pm[2] = 0.5*(p1[2]+p2[2]);
|
||||
pm[3] = 0.5*(p1[3]+p2[3]);
|
||||
pm[4] = 0.5*(p1[4]+p2[4]);
|
||||
|
||||
p1 = controlPoint[1];
|
||||
p2 = controlPoint[3];
|
||||
pm = controlPoint[2];
|
||||
|
||||
pm[0] = 0.5*(p1[0]+p2[0]);
|
||||
pm[1] = 0.5*(p1[1]+p2[1]);
|
||||
pm[2] = 0.5*(p1[2]+p2[2]);
|
||||
pm[3] = 0.5*(p1[3]+p2[3]);
|
||||
pm[4] = 0.5*(p1[4]+p2[4]);
|
||||
|
||||
/* The point in pm[] is a point located on the Bezier curve : it must be
|
||||
* added to the vertex array
|
||||
*/
|
||||
if (!__glcArrayInsert(data->vertexArray, rank+1, pm)) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nArc++; /* A new arc has been defined */
|
||||
}
|
||||
}
|
||||
|
||||
/* The array of control points must be emptied in order to be ready for the
|
||||
* next call to the de Casteljau routine
|
||||
*/
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* __glcdeCasteljauCubic :
|
||||
* renders cubic Bezier curves using the de Casteljau subdivision algorithm
|
||||
*
|
||||
* See also remarks about __glcdeCasteljauConic.
|
||||
*/
|
||||
int __glcdeCasteljauCubic(void *inUserData)
|
||||
{
|
||||
__GLCrendererData *data = (__GLCrendererData *) inUserData;
|
||||
GLfloat* vector = data->vector;
|
||||
GLfloat(*controlPoint)[5] = NULL;
|
||||
GLint nArc = 1, arc = 0, rank = 0;
|
||||
int iter = 0;
|
||||
GLfloat* cp = (GLfloat*)__glcArrayInsertCell(data->controlPoints,
|
||||
GLC_ARRAY_LENGTH(data->controlPoints), 4);
|
||||
|
||||
if (!cp) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Append the control points to the vertex array */
|
||||
memcpy(cp, vector, 2 * sizeof(GLfloat));
|
||||
__glcComputePixelCoordinates(cp, data);
|
||||
|
||||
/* Append the first vertex of the curve to the vertex array */
|
||||
rank = GLC_ARRAY_LENGTH(data->vertexArray);
|
||||
if (!__glcArrayAppend(data->vertexArray, cp)) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Build the array of the control points */
|
||||
for (iter = 0; iter < 3; iter++) {
|
||||
cp += 5;
|
||||
vector += 2;
|
||||
memcpy(cp, vector, 2 * sizeof(GLfloat));
|
||||
__glcComputePixelCoordinates(cp, data);
|
||||
}
|
||||
|
||||
/* controlPoint[] must be computed there because data->controlPoints->data
|
||||
* may have been modified by a realloc() in __glcArrayInsert()
|
||||
*/
|
||||
controlPoint = (GLfloat(*)[5])GLC_ARRAY_DATA(data->controlPoints);
|
||||
|
||||
/* Here the de Casteljau algorithm begins */
|
||||
for (iter = 0; (iter < GLC_MAX_ITER) && (arc != nArc); iter++) {
|
||||
GLfloat ax = controlPoint[0][2];
|
||||
GLfloat ay = controlPoint[0][3];
|
||||
GLfloat aw = controlPoint[0][4];
|
||||
GLfloat abx = controlPoint[3][2]*aw - ax*controlPoint[3][4];
|
||||
GLfloat aby = controlPoint[3][3]*aw - ay*controlPoint[3][4];
|
||||
/* For the middle control point, compute its chordal distance that is its
|
||||
* distance from the segment AB
|
||||
*/
|
||||
GLfloat mw = controlPoint[1][4];
|
||||
GLfloat s = ((controlPoint[1][2]*aw - ax*mw) * aby
|
||||
- (controlPoint[1][3]*aw - ay*mw) * abx)
|
||||
/ (aw * mw);
|
||||
GLfloat dmax = s * s;
|
||||
GLfloat d;
|
||||
|
||||
mw = controlPoint[2][4];
|
||||
s = ((controlPoint[2][2]*aw - ax*mw) * aby
|
||||
- (controlPoint[2][3]*aw - ay*mw) * abx)
|
||||
/ (aw * mw);
|
||||
d = s * s;
|
||||
|
||||
dmax = d > dmax ? d : dmax;
|
||||
|
||||
if (dmax < data->tolerance * (abx * abx + aby *aby)) {
|
||||
arc++; /* Process the next arc */
|
||||
controlPoint = ((GLfloat(*)[5])GLC_ARRAY_DATA(data->controlPoints))+3*arc;
|
||||
/* Update the place where new vertices will be inserted in the vertex
|
||||
* array
|
||||
*/
|
||||
rank++;
|
||||
}
|
||||
else {
|
||||
/* Split an arc into two smaller arcs (this is the actual de Casteljau
|
||||
* algorithm)
|
||||
*/
|
||||
GLfloat *p1, *p2, *p3;
|
||||
GLfloat *pm = (GLfloat*)__glcArrayInsertCell(data->controlPoints,
|
||||
3*arc+1, 3);
|
||||
|
||||
if (!pm) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* controlPoint[] must be updated there because
|
||||
* data->controlPoints->data may have been modified by a realloc() in
|
||||
* __glcArrayInsert()
|
||||
*/
|
||||
controlPoint = ((GLfloat(*)[5])GLC_ARRAY_DATA(data->controlPoints))+3*arc;
|
||||
|
||||
p1 = controlPoint[0];
|
||||
p2 = controlPoint[4];
|
||||
pm = controlPoint[1];
|
||||
|
||||
pm[0] = 0.5*(p1[0]+p2[0]);
|
||||
pm[1] = 0.5*(p1[1]+p2[1]);
|
||||
pm[2] = 0.5*(p1[2]+p2[2]);
|
||||
pm[3] = 0.5*(p1[3]+p2[3]);
|
||||
pm[4] = 0.5*(p1[4]+p2[4]);
|
||||
|
||||
p3 = controlPoint[5];
|
||||
pm = controlPoint[2];
|
||||
|
||||
pm[0] = 0.25*(p1[0]+2*p2[0]+p3[0]);
|
||||
pm[1] = 0.25*(p1[1]+2*p2[1]+p3[1]);
|
||||
pm[2] = 0.25*(p1[2]+2*p2[2]+p3[2]);
|
||||
pm[3] = 0.25*(p1[3]+2*p2[3]+p3[3]);
|
||||
pm[4] = 0.25*(p1[4]+2*p2[4]+p3[4]);
|
||||
|
||||
p1 = controlPoint[6];
|
||||
p2 = controlPoint[5];
|
||||
pm = controlPoint[5];
|
||||
|
||||
pm[0] = 0.5*(p1[0]+p2[0]);
|
||||
pm[1] = 0.5*(p1[1]+p2[1]);
|
||||
pm[2] = 0.5*(p1[2]+p2[2]);
|
||||
pm[3] = 0.5*(p1[3]+p2[3]);
|
||||
pm[4] = 0.5*(p1[4]+p2[4]);
|
||||
|
||||
p1 = controlPoint[4];
|
||||
p2 = controlPoint[5];
|
||||
p3 = controlPoint[6];
|
||||
pm = controlPoint[4];
|
||||
|
||||
pm[0] = 0.25*(p1[0]+4*p2[0]-p3[0]);
|
||||
pm[1] = 0.25*(p1[1]+4*p2[1]-p3[1]);
|
||||
pm[2] = 0.25*(p1[2]+4*p2[2]-p3[2]);
|
||||
pm[3] = 0.25*(p1[3]+4*p2[3]-p3[3]);
|
||||
pm[4] = 0.25*(p1[4]+4*p2[4]-p3[4]);
|
||||
|
||||
p1 = controlPoint[2];
|
||||
p2 = controlPoint[4];
|
||||
pm = controlPoint[3];
|
||||
|
||||
pm[0] = 0.5*(p1[0]+p2[0]);
|
||||
pm[1] = 0.5*(p1[1]+p2[1]);
|
||||
pm[2] = 0.5*(p1[2]+p2[2]);
|
||||
pm[3] = 0.5*(p1[3]+p2[3]);
|
||||
pm[4] = 0.5*(p1[4]+p2[4]);
|
||||
|
||||
/* The point in pm[] is a point located on the Bezier curve : it must be
|
||||
* added to the vertex array
|
||||
*/
|
||||
if (!__glcArrayInsert(data->vertexArray, rank+1, pm)) {
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
nArc++; /* A new arc has been defined */
|
||||
}
|
||||
}
|
||||
|
||||
/* The array of control points must be emptied in order to be ready for the
|
||||
* next call to the de Casteljau routine
|
||||
*/
|
||||
GLC_ARRAY_LENGTH(data->controlPoints) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Callback function that is called by the GLU when it is tesselating a
|
||||
* polygon.
|
||||
*/
|
||||
static void CALLBACK __glcCombineCallback(GLdouble coords[3],
|
||||
void* GLC_UNUSED_ARG(vertex_data[4]),
|
||||
GLfloat GLC_UNUSED_ARG(weight[4]),
|
||||
void** outData, void* inUserData)
|
||||
{
|
||||
__GLCrendererData *data = (__GLCrendererData*)inUserData;
|
||||
GLfloat vertex[2];
|
||||
/* Evil hack for 32/64 bits compatibility */
|
||||
union {
|
||||
void* ptr;
|
||||
GLuint i;
|
||||
} uintInPtr;
|
||||
|
||||
/* Compute the new vertex and append it to the vertex array */
|
||||
vertex[0] = (GLfloat)coords[0];
|
||||
vertex[1] = (GLfloat)coords[1];
|
||||
if (!__glcArrayAppend(data->vertexArray, vertex))
|
||||
return;
|
||||
|
||||
/* Returns the index of the new vertex in the vertex array */
|
||||
uintInPtr.i = GLC_ARRAY_LENGTH(data->vertexArray)-1;
|
||||
*outData = uintInPtr.ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Callback function that is called by the GLU when it is rendering the
|
||||
* tesselated polygon. This function is needed to convert the indices of the
|
||||
* vertex array into the coordinates of the vertex.
|
||||
*/
|
||||
static void CALLBACK __glcVertexCallback(void* vertex_data, void* inUserData)
|
||||
{
|
||||
__GLCrendererData *data = (__GLCrendererData*)inUserData;
|
||||
__GLCgeomBatch *geomBatch =
|
||||
((__GLCgeomBatch*)GLC_ARRAY_DATA(data->geomBatches));
|
||||
/* Evil hack for 32/64 bits compatibility */
|
||||
union {
|
||||
void* ptr;
|
||||
GLuint i;
|
||||
} uintInPtr;
|
||||
|
||||
geomBatch += GLC_ARRAY_LENGTH(data->geomBatches) - 1;
|
||||
|
||||
uintInPtr.ptr = vertex_data;
|
||||
geomBatch->start = (uintInPtr.i < geomBatch->start) ? uintInPtr.i :
|
||||
geomBatch->start;
|
||||
geomBatch->end = (uintInPtr.i > geomBatch->end) ? uintInPtr.i :
|
||||
geomBatch->end;
|
||||
if (!__glcArrayAppend(data->vertexIndices, &uintInPtr.i))
|
||||
return;
|
||||
|
||||
geomBatch->length++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void CALLBACK __glcBeginCallback(GLenum mode, void* inUserData)
|
||||
{
|
||||
__GLCrendererData *data = (__GLCrendererData*)inUserData;
|
||||
__GLCgeomBatch geomBatch;
|
||||
|
||||
geomBatch.mode = mode;
|
||||
geomBatch.length = 0;
|
||||
geomBatch.start = 0xffffffff;
|
||||
geomBatch.end = 0;
|
||||
|
||||
__glcArrayAppend(data->geomBatches, &geomBatch);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Callback function that is called by the GLU whenever an error occur during
|
||||
* the tesselation of the polygon.
|
||||
*/
|
||||
static void CALLBACK __glcCallbackError(GLenum GLC_UNUSED_ARG(inErrorCode))
|
||||
{
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Function called by __glcRenderChar() and that performs the actual rendering
|
||||
* for the GLC_LINE and the GLC_TRIANGLE types. It transforms the outlines of
|
||||
* the glyph in polygon contour. If the rendering type is GLC_LINE then the
|
||||
* contour is rendered as is and if the rendering type is GLC_TRIANGLE then the
|
||||
* contour defines a polygon that is tesselated in triangles by the GLU library
|
||||
* before being rendered.
|
||||
*/
|
||||
void __glcRenderCharScalable(const __GLCfont* inFont,
|
||||
const __GLCcontext* inContext,
|
||||
GLfloat* inTransformMatrix, const GLfloat inScaleX,
|
||||
const GLfloat inScaleY, __GLCglyph* inGlyph)
|
||||
{
|
||||
__GLCrendererData rendererData;
|
||||
GLfloat identityMatrix[16] = {1., 0., 0., 0., 0., 1., 0., 0., 0., 0.,
|
||||
1., 0., 0., 0., 0., 1.};
|
||||
GLfloat sx64 = 64. * inScaleX;
|
||||
GLfloat sy64 = 64. * inScaleY;
|
||||
int objectIndex = 0;
|
||||
GLfloat orientation = 1.f;
|
||||
|
||||
rendererData.vertexArray = inContext->vertexArray;
|
||||
rendererData.controlPoints = inContext->controlPoints;
|
||||
rendererData.endContour = inContext->endContour;
|
||||
rendererData.vertexIndices = inContext->vertexIndices;
|
||||
rendererData.geomBatches = inContext->geomBatches;
|
||||
|
||||
if (inContext->enableState.extrude)
|
||||
orientation = -inTransformMatrix[11];
|
||||
|
||||
/* If no display list is planned to be built then compute distances in pixels
|
||||
* otherwise use the object space.
|
||||
*/
|
||||
if (!inContext->enableState.glObjects) {
|
||||
GLint viewport[4];
|
||||
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
rendererData.halfWidth = viewport[2] * 0.5;
|
||||
rendererData.halfHeight = viewport[3] * 0.5;
|
||||
rendererData.transformMatrix = inTransformMatrix;
|
||||
rendererData.transformMatrix[0] *= rendererData.halfWidth / sx64;
|
||||
rendererData.transformMatrix[4] *= rendererData.halfWidth / sy64;
|
||||
rendererData.transformMatrix[12] *= rendererData.halfWidth;
|
||||
rendererData.transformMatrix[1] *= rendererData.halfHeight / sx64;
|
||||
rendererData.transformMatrix[5] *= rendererData.halfHeight / sy64;
|
||||
rendererData.transformMatrix[13] *= rendererData.halfHeight;
|
||||
rendererData.transformMatrix[2] /= sx64;
|
||||
rendererData.transformMatrix[3] /= sx64;
|
||||
rendererData.transformMatrix[6] /= sy64;
|
||||
rendererData.transformMatrix[7] /= sy64;
|
||||
|
||||
#if 0
|
||||
rendererData.tolerance = .25; /* Half pixel tolerance */
|
||||
#else
|
||||
rendererData.tolerance = 1.; /* Pixel tolerance */
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* Distances are computed in object space, so is the tolerance of the
|
||||
* de Casteljau algorithm.
|
||||
*/
|
||||
rendererData.tolerance = inContext->renderState.tolerance
|
||||
* sqrt(inScaleX * inScaleX + inScaleY * inScaleY) / sx64 / sy64;
|
||||
rendererData.halfWidth = 0.5;
|
||||
rendererData.halfHeight = 0.5;
|
||||
rendererData.transformMatrix = identityMatrix;
|
||||
rendererData.transformMatrix[0] /= sx64;
|
||||
rendererData.transformMatrix[5] /= sy64;
|
||||
}
|
||||
|
||||
/* Parse the outline of the glyph */
|
||||
if (!__glcFontOutlineDecompose(inFont, &rendererData, inContext))
|
||||
return;
|
||||
|
||||
if (!__glcArrayAppend(rendererData.endContour,
|
||||
&GLC_ARRAY_LENGTH(rendererData.vertexArray)))
|
||||
goto reset;
|
||||
|
||||
switch(inContext->renderState.renderStyle) {
|
||||
case GLC_LINE:
|
||||
objectIndex = 0;
|
||||
break;
|
||||
case GLC_TRIANGLE:
|
||||
objectIndex = inContext->enableState.extrude ? 3 : 2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepare the display list if needed. For optimization reasons, if we use
|
||||
* VBOs we build them for the 3 rendering modes (GLC_LINE, GLC_TRIANGLE,
|
||||
* extrusion) in a row. (Vertices are common to all rendering modes and
|
||||
* contours are common to GLC_LINE and extrude).
|
||||
*/
|
||||
if (inContext->enableState.glObjects) {
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
int i = 0;
|
||||
GLfloat (*vertexArray)[2] =
|
||||
(GLfloat(*)[2])GLC_ARRAY_DATA(rendererData.vertexArray);
|
||||
|
||||
inGlyph->nContour = GLC_ARRAY_LENGTH(rendererData.endContour) - 1;
|
||||
inGlyph->contours =
|
||||
(GLint*)__glcMalloc(GLC_ARRAY_SIZE(rendererData.endContour));
|
||||
if (!inGlyph->contours) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
goto reset;
|
||||
}
|
||||
memcpy(inGlyph->contours, GLC_ARRAY_DATA(rendererData.endContour),
|
||||
GLC_ARRAY_SIZE(rendererData.endContour));
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.vertexArray); i++) {
|
||||
vertexArray[i][0] /= sx64;
|
||||
vertexArray[i][1] /= sy64;
|
||||
}
|
||||
|
||||
glGenBuffersARB(1, &inGlyph->glObject[0]);
|
||||
glGenBuffersARB(1, &inGlyph->glObject[2]);
|
||||
if (!inGlyph->glObject[0] || !inGlyph->glObject[2]) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
inGlyph->nContour = 0;
|
||||
__glcFree(inGlyph->contours);
|
||||
inGlyph->contours= NULL;
|
||||
|
||||
if (inGlyph->glObject[0]) {
|
||||
glDeleteBuffersARB(1, &inGlyph->glObject[0]);
|
||||
inGlyph->glObject[0] = 0;
|
||||
}
|
||||
|
||||
if (inGlyph->glObject[2]) {
|
||||
glDeleteBuffersARB(1, &inGlyph->glObject[2]);
|
||||
inGlyph->glObject[2] = 0;
|
||||
}
|
||||
goto reset;
|
||||
}
|
||||
/* Create the VBO for GLC_LINE rendering mode */
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, inGlyph->glObject[0]);
|
||||
|
||||
glBufferDataARB(GL_ARRAY_BUFFER_ARB,
|
||||
GLC_ARRAY_SIZE(rendererData.vertexArray),
|
||||
GLC_ARRAY_DATA(rendererData.vertexArray),
|
||||
GL_STATIC_DRAW_ARB);
|
||||
|
||||
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, inGlyph->glObject[2]);
|
||||
}
|
||||
else {
|
||||
inGlyph->glObject[objectIndex] = glGenLists(1);
|
||||
if (!inGlyph->glObject[objectIndex]) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
goto reset;
|
||||
}
|
||||
|
||||
glNewList(inGlyph->glObject[objectIndex], GL_COMPILE);
|
||||
glScalef(1./sx64, 1./sy64, 1.);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tesselate the polygon defined by the contour returned by
|
||||
* __glcFontOutlineDecompose().
|
||||
*/
|
||||
if (inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
|| (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object)) {
|
||||
GLUtesselator *tess = gluNewTess();
|
||||
GLuint j = 0;
|
||||
int i = 0;
|
||||
GLuint* endContour = (GLuint*)GLC_ARRAY_DATA(rendererData.endContour);
|
||||
GLfloat (*vertexArray)[2] =
|
||||
(GLfloat(*)[2])GLC_ARRAY_DATA(rendererData.vertexArray);
|
||||
GLdouble coords[3] = {0., 0., 0.};
|
||||
|
||||
/* Initialize the GLU tesselator */
|
||||
gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
|
||||
gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE);
|
||||
|
||||
gluTessCallback(tess, GLU_TESS_ERROR,
|
||||
(void (CALLBACK *) (GLenum))__glcCallbackError);
|
||||
gluTessCallback(tess, GLU_TESS_VERTEX_DATA,
|
||||
(void (CALLBACK *) (void*, void*))__glcVertexCallback);
|
||||
gluTessCallback(tess, GLU_TESS_COMBINE_DATA,
|
||||
(void (CALLBACK *) (GLdouble[3], void*[4],
|
||||
GLfloat[4], void**, void*))
|
||||
__glcCombineCallback);
|
||||
gluTessCallback(tess, GLU_TESS_BEGIN_DATA,
|
||||
(void (CALLBACK *) (GLenum, void*))__glcBeginCallback);
|
||||
|
||||
gluTessNormal(tess, 0., 0., 1.);
|
||||
|
||||
/* Define the polygon geometry */
|
||||
gluTessBeginPolygon(tess, &rendererData);
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.endContour)-1; i++) {
|
||||
/* Evil hack for 32/64 bits compatibility */
|
||||
union {
|
||||
void* ptr;
|
||||
GLuint i;
|
||||
} uintInPtr;
|
||||
|
||||
gluTessBeginContour(tess);
|
||||
for (j = endContour[i]; j < endContour[i+1]; j++) {
|
||||
coords[0] = (GLdouble)vertexArray[j][0];
|
||||
coords[1] = (GLdouble)vertexArray[j][1];
|
||||
uintInPtr.i = j;
|
||||
gluTessVertex(tess, coords, uintInPtr.ptr);
|
||||
}
|
||||
gluTessEndContour(tess);
|
||||
}
|
||||
|
||||
/* Close the polygon and run the tesselation */
|
||||
gluTessEndPolygon(tess);
|
||||
|
||||
/* Free memory */
|
||||
gluDeleteTess(tess);
|
||||
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object) {
|
||||
inGlyph->nGeomBatch = GLC_ARRAY_LENGTH(rendererData.geomBatches);
|
||||
inGlyph->geomBatches =
|
||||
(__GLCgeomBatch*)__glcMalloc(GLC_ARRAY_SIZE(rendererData.geomBatches));
|
||||
|
||||
if (!inGlyph->geomBatches) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
glDeleteBuffersARB(1, &inGlyph->glObject[2]);
|
||||
inGlyph->glObject[2] = 0;
|
||||
goto reset;
|
||||
}
|
||||
|
||||
memcpy(inGlyph->geomBatches, GLC_ARRAY_DATA(rendererData.geomBatches),
|
||||
GLC_ARRAY_SIZE(rendererData.geomBatches));
|
||||
|
||||
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB,
|
||||
GLC_ARRAY_SIZE(rendererData.vertexIndices),
|
||||
GLC_ARRAY_DATA(rendererData.vertexIndices),
|
||||
GL_STATIC_DRAW_ARB);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the tesselation is done, the actual rendering for GLC_TRIANGLE
|
||||
* begins.
|
||||
*/
|
||||
if (inContext->renderState.renderStyle == GLC_TRIANGLE) {
|
||||
int i = 0;
|
||||
__GLCgeomBatch* geomBatch =
|
||||
(__GLCgeomBatch*)GLC_ARRAY_DATA(rendererData.geomBatches);
|
||||
GLboolean extrude = GL_FALSE;
|
||||
|
||||
do {
|
||||
GLuint* vertexIndices = NULL;
|
||||
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object)
|
||||
glVertexPointer(2, GL_FLOAT, 0, NULL);
|
||||
else {
|
||||
vertexIndices = (GLuint*)GLC_ARRAY_DATA(rendererData.vertexIndices);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, 0,
|
||||
GLC_ARRAY_DATA(rendererData.vertexArray));
|
||||
}
|
||||
|
||||
if (inContext->enableState.glObjects || (orientation > 0.f))
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.geomBatches); i++) {
|
||||
glDrawRangeElements(geomBatch[i].mode, geomBatch[i].start,
|
||||
geomBatch[i].end, geomBatch[i].length,
|
||||
GL_UNSIGNED_INT, vertexIndices);
|
||||
vertexIndices += geomBatch[i].length;
|
||||
}
|
||||
|
||||
/* If the extrusion is selected, the vertex array of the GLC_TRIANGLE will
|
||||
* be rendered a second time translated along the axis.
|
||||
*/
|
||||
if (inContext->enableState.extrude) {
|
||||
if (extrude)
|
||||
glTranslatef(0.f, 0.f, 1.f);
|
||||
else {
|
||||
glNormal3f(0.f, 0.f, -1.f);
|
||||
glTranslatef(0.f, 0.f, -1.f);
|
||||
orientation = -orientation;
|
||||
}
|
||||
extrude = (!extrude);
|
||||
}
|
||||
} while (extrude);
|
||||
}
|
||||
|
||||
/* For extruded glyphes : close the contours */
|
||||
if ((inContext->renderState.renderStyle == GLC_TRIANGLE
|
||||
&& inContext->enableState.extrude)
|
||||
|| (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object)) {
|
||||
GLfloat ax = 0.f, bx = 0.f, ay = 0.f, by = 0.f;
|
||||
GLfloat nx = 0.f, ny = 0.f, n0x = 0.f, n0y = 0.f, length = 0.f;
|
||||
GLuint* endContour = (GLuint*)GLC_ARRAY_DATA(rendererData.endContour);
|
||||
GLfloat (*vertexArray)[2] =
|
||||
(GLfloat(*)[2])GLC_ARRAY_DATA(rendererData.vertexArray);
|
||||
int i = 0;
|
||||
GLuint j = 0;
|
||||
GLfloat* extrudeArray = NULL;
|
||||
GLfloat* interleavedArray = NULL;
|
||||
|
||||
/* Prepare the VBO for the contour */
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object) {
|
||||
GLuint nVertices = 0;
|
||||
|
||||
glGenBuffersARB(1, &inGlyph->glObject[3]);
|
||||
if (!inGlyph->glObject[3]) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
goto reset;
|
||||
}
|
||||
|
||||
/* Compute the total number of vertices that will be stored in the VBO */
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.endContour)-1; i++)
|
||||
nVertices += endContour[i+1] - endContour[i] + 1;
|
||||
|
||||
assert(nVertices);
|
||||
|
||||
/* The array stores (3D vertices + 3D normal) * 2 for each point of the
|
||||
* contour.
|
||||
*/
|
||||
extrudeArray = (GLfloat*)__glcMalloc(12 * sizeof(GLfloat) * nVertices);
|
||||
if (!extrudeArray) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
glDeleteBuffers(1, &inGlyph->glObject[3]);
|
||||
inGlyph->glObject[3] = 0;
|
||||
goto reset;
|
||||
}
|
||||
|
||||
interleavedArray = extrudeArray;
|
||||
}
|
||||
|
||||
/* Compute the vertices and the normals of the contour */
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.endContour)-1; i++) {
|
||||
if (!(inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object))
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
|
||||
for (j = endContour[i]; j < endContour[i+1]; j++) {
|
||||
if (j == endContour[i]) {
|
||||
ax = vertexArray[endContour[i+1]-1][0];
|
||||
ay = vertexArray[endContour[i+1]-1][1];
|
||||
bx = vertexArray[j+1][0];
|
||||
by = vertexArray[j+1][1];
|
||||
n0x = ay - by;
|
||||
n0y = bx - ax;
|
||||
}
|
||||
else if (j == (endContour[i+1] - 1)) {
|
||||
ax = vertexArray[j-1][0];
|
||||
ay = vertexArray[j-1][1];
|
||||
bx = vertexArray[endContour[i]][0];
|
||||
by = vertexArray[endContour[i]][1];
|
||||
}
|
||||
else {
|
||||
ax = vertexArray[j-1][0];
|
||||
ay = vertexArray[j-1][1];
|
||||
bx = vertexArray[j+1][0];
|
||||
by = vertexArray[j+1][1];
|
||||
}
|
||||
|
||||
nx = ay - by;
|
||||
ny = bx - ax;
|
||||
length = sqrt(nx*nx + ny*ny);
|
||||
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object) {
|
||||
interleavedArray[0] = nx / length;
|
||||
interleavedArray[1] = nx / length;
|
||||
interleavedArray[2] = 0.f;
|
||||
interleavedArray[3] = vertexArray[j][0];
|
||||
interleavedArray[4] = vertexArray[j][1];
|
||||
interleavedArray[5] = 0.f;
|
||||
memcpy(interleavedArray + 6, interleavedArray, 5 * sizeof(GLfloat));
|
||||
interleavedArray[11] = -1.f;
|
||||
interleavedArray += 12;
|
||||
}
|
||||
else {
|
||||
glNormal3f(nx/length, ny/length, 0.f);
|
||||
glVertex2fv(vertexArray[j]);
|
||||
glVertex3f(vertexArray[j][0], vertexArray[j][1], -1.f);
|
||||
}
|
||||
}
|
||||
length = sqrt(n0x*n0x + n0y*n0y);
|
||||
|
||||
/* Close the contour (repeat the first vertex at the end of the array) */
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object) {
|
||||
interleavedArray[0] = n0x / length;
|
||||
interleavedArray[1] = n0x / length;
|
||||
interleavedArray[2] = 0.f;
|
||||
interleavedArray[3] = vertexArray[endContour[i]][0];
|
||||
interleavedArray[4] = vertexArray[endContour[i]][1];
|
||||
interleavedArray[5] = 0.f;
|
||||
memcpy(interleavedArray + 6, interleavedArray, 5 * sizeof(GLfloat));
|
||||
interleavedArray[11] = -1.f;
|
||||
interleavedArray += 12;
|
||||
}
|
||||
else {
|
||||
glNormal3f(n0x/length, n0y/length, 0.f);
|
||||
glVertex2fv(vertexArray[endContour[i]]);
|
||||
glVertex3f(vertexArray[endContour[i]][0],
|
||||
vertexArray[endContour[i]][1], -1.f);
|
||||
}
|
||||
|
||||
if (!(inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object))
|
||||
glEnd();
|
||||
}
|
||||
|
||||
/* Create the VBO of the contour */
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, inGlyph->glObject[3]);
|
||||
glBufferDataARB(GL_ARRAY_BUFFER_ARB, (interleavedArray - extrudeArray)
|
||||
* sizeof(GLfloat), extrudeArray,
|
||||
GL_STATIC_DRAW_ARB);
|
||||
|
||||
__glcFree(extrudeArray);
|
||||
|
||||
/* Render the contour */
|
||||
if (inContext->enableState.extrude) {
|
||||
glInterleavedArrays(GL_N3F_V3F, 0, NULL);
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.endContour)-1; i++)
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, endContour[i] * 2,
|
||||
(endContour[i+1] - endContour[i] + 1) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
glNormal3f(0.f, 0.f, 1.f);
|
||||
}
|
||||
|
||||
if (inContext->renderState.renderStyle == GLC_LINE) {
|
||||
/* For GLC_LINE, there is no need to tesselate. The vertices are contained
|
||||
* in an array so we use the OpenGL function glDrawArrays().
|
||||
*/
|
||||
int i = 0;
|
||||
int* endContour = (int*)GLC_ARRAY_DATA(rendererData.endContour);
|
||||
|
||||
if (inContext->enableState.glObjects && GLEW_ARB_vertex_buffer_object) {
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, inGlyph->glObject[0]);
|
||||
glVertexPointer(2, GL_FLOAT, 0, NULL);
|
||||
}
|
||||
else
|
||||
glVertexPointer(2, GL_FLOAT, 0, GLC_ARRAY_DATA(rendererData.vertexArray));
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(rendererData.endContour)-1; i++)
|
||||
glDrawArrays(GL_LINE_LOOP, endContour[i], endContour[i+1]-endContour[i]);
|
||||
}
|
||||
|
||||
if (inContext->enableState.glObjects && !GLEW_ARB_vertex_buffer_object) {
|
||||
glScalef(sx64, sy64, 1.);
|
||||
glEndList();
|
||||
glCallList(inGlyph->glObject[objectIndex]);
|
||||
}
|
||||
|
||||
reset:
|
||||
GLC_ARRAY_LENGTH(inContext->vertexArray) = 0;
|
||||
GLC_ARRAY_LENGTH(inContext->endContour) = 0;
|
||||
GLC_ARRAY_LENGTH(inContext->vertexIndices) = 0;
|
||||
GLC_ARRAY_LENGTH(inContext->geomBatches) = 0;
|
||||
}
|
|
@ -0,0 +1,602 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the routines used to render characters with textures.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#if defined __APPLE__ && defined __MACH__
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
|
||||
|
||||
/* This function is called when a glyph is destroyed, the atlas element is then
|
||||
* released.
|
||||
*/
|
||||
void __glcReleaseAtlasElement(__GLCatlasElement* This,
|
||||
__GLCcontext* inContext)
|
||||
{
|
||||
FT_ListNode node = (FT_ListNode)This;
|
||||
|
||||
/* Put the atlas element at the tail of the list so that its position is used
|
||||
* as soon as possible.
|
||||
*/
|
||||
FT_List_Remove(&inContext->atlasList, node);
|
||||
FT_List_Add(&inContext->atlasList, node);
|
||||
This->glyph = NULL; /* The glyph will be destroyed so clear the pointer */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function gets some room in the texture atlas for a new glyph 'inGlyph'.
|
||||
* Eventually it creates the texture atlas, if it does not exist yet.
|
||||
*/
|
||||
static GLboolean __glcTextureAtlasGetPosition(__GLCcontext* inContext,
|
||||
__GLCglyph* inGlyph)
|
||||
{
|
||||
__GLCatlasElement* atlasNode = NULL;
|
||||
|
||||
/* Test if the atlas already exists. If not, create it. */
|
||||
if (!inContext->atlas.id) {
|
||||
int size = 1024; /* Initial try with a 1024x1024 texture */
|
||||
int i = 0;
|
||||
GLint format = 0;
|
||||
GLint level = 0;
|
||||
void * buffer = NULL;
|
||||
|
||||
/* Not all gfx card are able to use 1024x1024 textures (especially old ones
|
||||
* like 3dfx's). Moreover, the texture memory may be scarce when our texture
|
||||
* will be created, so we try several texture sizes : first 1024x1024 then
|
||||
* if it fails, we try 512x512 then 256x256. All gfx cards support 256x256
|
||||
* textures so if it fails with this texture size, that is because we ran
|
||||
* out of texture memory. In such a case, there is nothing we can do, so the
|
||||
* routine aborts with GLC_RESOURCE_ERROR raised.
|
||||
*/
|
||||
for (i = 0; i < 3; i++) {
|
||||
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_ALPHA8, size,
|
||||
size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
|
||||
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_COMPONENTS,
|
||||
&format);
|
||||
if (format)
|
||||
break;
|
||||
|
||||
size >>= 1;
|
||||
}
|
||||
|
||||
/* Out of texture memory : abortion */
|
||||
if (i == 3) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
buffer = __glcMalloc(size * size);
|
||||
if (!buffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
memset(buffer, 0, size * size);
|
||||
|
||||
/* Create the texture atlas structure. The texture is divided in small
|
||||
* square areas of GLC_TEXTURE_SIZE x GLC_TEXTURE_SIZE, each of which will
|
||||
* contain a different glyph.
|
||||
* TODO: Allow the user to change GLC_TEXTURE_SIZE rather than using a fixed
|
||||
* value.
|
||||
*/
|
||||
glGenTextures(1, &inContext->atlas.id);
|
||||
inContext->atlas.width = size;
|
||||
inContext->atlas.height = size;
|
||||
inContext->atlasWidth = size / GLC_TEXTURE_SIZE;
|
||||
inContext->atlasHeight = size / GLC_TEXTURE_SIZE;
|
||||
inContext->atlasCount = 0;
|
||||
glBindTexture(GL_TEXTURE_2D, inContext->atlas.id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, size,
|
||||
size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
/* Create the mipmap structure of the texture atlas, no matter if GLC_MIPMAP
|
||||
* is enabled or not.
|
||||
*/
|
||||
while (size > 1) {
|
||||
size >>= 1;
|
||||
level++;
|
||||
glTexImage2D(GL_TEXTURE_2D, level, GL_ALPHA8, size,
|
||||
size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
/* Use trilinear filtering if GLC_MIPMAP is enabled.
|
||||
* Otherwise use bilinear filtering.
|
||||
*/
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
|
||||
/* The intent of this code is to work around an ugly bug of the Intel GMA
|
||||
* 965 (or X3000) drivers on Linux. On those crappy drivers a 2nd call to
|
||||
* glTexSubImage2D() completely clears the texture removing by the way the
|
||||
* first character stored in the texture...
|
||||
* This workaround displays a dummy character in order to deceive the
|
||||
* stupid drivers. Note that I tried to reduce the code to the minimum : it
|
||||
* seems that if any line below is removed, the workaround no longer works
|
||||
* around the f***ing bug.
|
||||
*/
|
||||
size = GLC_TEXTURE_SIZE;
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size, size, GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE, buffer);
|
||||
level = 0;
|
||||
while (size > 2) {
|
||||
size >>= 1;
|
||||
level++;
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, size, size, GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE, buffer);
|
||||
}
|
||||
|
||||
if (GLEW_VERSION_1_2 || GLEW_SGIS_texture_lod)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glNormal3f(0.f, 0.f, 1.f);
|
||||
glTexCoord2f(0.f, 0.f);
|
||||
glVertex2f(0.f, 0.f);
|
||||
glTexCoord2f(0.f, 1.f);
|
||||
glVertex2f(0.f, .5f);
|
||||
glTexCoord2f(1.f, 1.f);
|
||||
glVertex2f(.5f, .5f);
|
||||
glTexCoord2f(1.f, 0.f);
|
||||
glVertex2f(.5f, 0.f);
|
||||
glEnd();
|
||||
/* End of the workaround for the crappy open source drivers for Intel chips
|
||||
*/
|
||||
__glcFree(buffer);
|
||||
|
||||
if (inContext->enableState.mipmap)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR_MIPMAP_LINEAR);
|
||||
else
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
GL_LINEAR);
|
||||
}
|
||||
|
||||
/* At this stage, we want to get a free area in the texture atlas in order to
|
||||
* store a new glyph. Two situations may occur : the atlas is full or not
|
||||
*/
|
||||
if (inContext->atlasCount == inContext->atlasWidth * inContext->atlasHeight) {
|
||||
/* The texture atlas is full. We must release an area to re-use it.
|
||||
* We get the glyph that has not been used for the longer time (that is the
|
||||
* tail element of atlasList).
|
||||
*/
|
||||
atlasNode = (__GLCatlasElement*)inContext->atlasList.tail;
|
||||
assert(atlasNode);
|
||||
|
||||
if (atlasNode->glyph) {
|
||||
/* Release the texture area of the glyph */
|
||||
__glcGlyphDestroyTexture(atlasNode->glyph, inContext);
|
||||
}
|
||||
/* Put the texture area at the head of the list otherwise we will use the
|
||||
* same texture element over and over again each time that we need to
|
||||
* release a texture area.
|
||||
*/
|
||||
FT_List_Up(&inContext->atlasList, (FT_ListNode)atlasNode);
|
||||
}
|
||||
else {
|
||||
/* The texture atlas is not full. We create a new texture area and we store
|
||||
* its definition in atlas list.
|
||||
*/
|
||||
atlasNode = (__GLCatlasElement*)__glcMalloc(sizeof(__GLCatlasElement));
|
||||
if (!atlasNode) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
atlasNode->node.data = atlasNode;
|
||||
atlasNode->position = inContext->atlasCount++;
|
||||
FT_List_Insert(&inContext->atlasList, (FT_ListNode)atlasNode);
|
||||
}
|
||||
|
||||
/* Update the texture element */
|
||||
atlasNode->glyph = inGlyph;
|
||||
inGlyph->textureObject = atlasNode;
|
||||
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
/* Create a VBO, if none exists yet */
|
||||
if (!inContext->atlas.bufferObjectID) {
|
||||
glGenBuffersARB(1, &inContext->atlas.bufferObjectID);
|
||||
if (!inContext->atlas.bufferObjectID) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
/* Even though we failed to create a VBO ID, the rendering of the glyph
|
||||
* can be processed without VBO, so we return GL_TRUE.
|
||||
*/
|
||||
return GL_TRUE;
|
||||
}
|
||||
}
|
||||
/* Bind the buffer and define/update its size */
|
||||
glBindBufferARB(GL_ARRAY_BUFFER_ARB, inContext->atlas.bufferObjectID);
|
||||
}
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* For immediate rendering mode (that is when GLC_GL_OBJECTS is disabled), this
|
||||
* function returns a texture that will store the glyph that is intended to be
|
||||
* rendered. If the texture does not exist yet, it is created.
|
||||
*/
|
||||
static GLboolean __glcTextureGetImmediate(__GLCcontext* inContext,
|
||||
const GLsizei inWidth,
|
||||
const GLsizei inHeight)
|
||||
{
|
||||
GLint format = 0;
|
||||
GLsizei width = inWidth;
|
||||
GLsizei height = inHeight;
|
||||
|
||||
/* Check if a texture exists to store the glyph */
|
||||
if (inContext->texture.id) {
|
||||
/* Check if the texture size is large enough to store the glyph */
|
||||
if ((inWidth > inContext->texture.width)
|
||||
|| (inHeight > inContext->texture.height)) {
|
||||
/* The texture is not large enough so we destroy the current texture */
|
||||
glDeleteTextures(1, &inContext->texture.id);
|
||||
width = (inWidth > inContext->texture.width) ?
|
||||
inWidth : inContext->texture.width;
|
||||
height = (inHeight > inContext->texture.height) ?
|
||||
inHeight : inContext->texture.height;
|
||||
inContext->texture.id = 0;
|
||||
inContext->texture.width = 0;
|
||||
inContext->texture.height = 0;
|
||||
}
|
||||
else {
|
||||
/* The texture is large enough, it is already bound to the current
|
||||
* GL context.
|
||||
*/
|
||||
return GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (GLEW_ARB_pixel_buffer_object)
|
||||
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
|
||||
|
||||
/* Check if a new texture can be created */
|
||||
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_ALPHA8, width, height, 0, GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_COMPONENTS,
|
||||
&format);
|
||||
/* TODO: If the texture creation fails, try with a smaller size */
|
||||
if (!format) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
/* Create a texture object and make it current */
|
||||
glGenTextures(1, &inContext->texture.id);
|
||||
glBindTexture(GL_TEXTURE_2D, inContext->texture.id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, width, height, 0, GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
/* For immediate mode rendering, always use bilinear filtering even if
|
||||
* GLC_MIPMAP is enabled : we have determined the size of the glyph when it
|
||||
* will be rendered on the screen and the texture size has been defined
|
||||
* accordingly. Hence the mipmap levels would not be used, so there is no
|
||||
* point in spending time to compute them.
|
||||
*/
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||
|
||||
inContext->texture.width = width;
|
||||
inContext->texture.height = height;
|
||||
|
||||
if (GLEW_ARB_pixel_buffer_object) {
|
||||
/* Create a PBO, if none exists yet */
|
||||
if (!inContext->texture.bufferObjectID) {
|
||||
glGenBuffersARB(1, &inContext->texture.bufferObjectID);
|
||||
if (!inContext->texture.bufferObjectID) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
/* Even though we failed to create a PBO ID, the rendering of the glyph
|
||||
* can be processed without PBO, so we return GL_TRUE.
|
||||
*/
|
||||
return GL_TRUE;
|
||||
}
|
||||
}
|
||||
/* Bind the buffer and define/update its size */
|
||||
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB,
|
||||
inContext->texture.bufferObjectID);
|
||||
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, width * height, NULL,
|
||||
GL_STREAM_DRAW_ARB);
|
||||
}
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Internal function that renders glyph in textures :
|
||||
* 'inCode' must be given in UCS-4 format
|
||||
*/
|
||||
void __glcRenderCharTexture(const __GLCfont* inFont, __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY,
|
||||
__GLCglyph* inGlyph)
|
||||
{
|
||||
GLint level = 0;
|
||||
GLint texX = 0, texY = 0;
|
||||
GLint pixWidth = 0, pixHeight = 0;
|
||||
void* pixBuffer = NULL;
|
||||
GLint pixBoundingBox[4] = {0, 0, 0, 0};
|
||||
int minSize = (GLEW_VERSION_1_2 || GLEW_SGIS_texture_lod) ? 2 : 1;
|
||||
GLfloat texWidth = 0.f, texHeight = 0.f;
|
||||
|
||||
if (inContext->enableState.glObjects) {
|
||||
__GLCatlasElement* atlasNode = NULL;
|
||||
|
||||
if (!__glcTextureAtlasGetPosition(inContext, inGlyph))
|
||||
return;
|
||||
|
||||
/* Compute the size of the pixmap where the glyph will be rendered */
|
||||
atlasNode = inGlyph->textureObject;
|
||||
|
||||
__glcFontGetBitmapSize(inFont, &pixWidth, &pixHeight, inScaleX, inScaleY, 0,
|
||||
pixBoundingBox, inContext);
|
||||
|
||||
texWidth = inContext->atlas.width;
|
||||
texHeight = inContext->atlas.height;
|
||||
texY = (atlasNode->position / inContext->atlasWidth);
|
||||
texX = (atlasNode->position - texY*inContext->atlasWidth)*GLC_TEXTURE_SIZE;
|
||||
texY *= GLC_TEXTURE_SIZE;
|
||||
}
|
||||
else {
|
||||
int factor = 0;
|
||||
|
||||
/* Try several texture size until we are able to create one */
|
||||
do {
|
||||
if (!__glcFontGetBitmapSize(inFont, &pixWidth, &pixHeight, inScaleX,
|
||||
inScaleY, factor, pixBoundingBox, inContext))
|
||||
return;
|
||||
|
||||
factor = 1;
|
||||
} while (!__glcTextureGetImmediate(inContext, pixWidth, pixHeight));
|
||||
|
||||
texWidth = inContext->texture.width;
|
||||
texHeight = inContext->texture.height;
|
||||
texX = 0;
|
||||
texY = 0;
|
||||
}
|
||||
|
||||
if (!inContext->texture.bufferObjectID || inContext->enableState.glObjects) {
|
||||
pixBuffer = (GLubyte *)__glcMalloc(pixWidth * pixHeight);
|
||||
if (!pixBuffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the texture */
|
||||
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
|
||||
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
/* Iterate on the powers of 2 in order to build the mipmap */
|
||||
do {
|
||||
if (GLEW_ARB_pixel_buffer_object && !inContext->enableState.glObjects) {
|
||||
pixBuffer = (GLubyte *)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB,
|
||||
GL_WRITE_ONLY_ARB);
|
||||
if (!pixBuffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* render the glyph */
|
||||
if (!__glcFaceDescGetBitmap(inFont->faceDesc, pixWidth, pixHeight,
|
||||
pixBuffer, inContext)) {
|
||||
glPopClientAttrib();
|
||||
|
||||
if (GLEW_ARB_pixel_buffer_object && !inContext->enableState.glObjects)
|
||||
glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
|
||||
else
|
||||
__glcFree(pixBuffer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (GLEW_ARB_pixel_buffer_object && !inContext->enableState.glObjects) {
|
||||
glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
|
||||
pixBuffer = NULL;
|
||||
}
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, texX >> level, texY >> level,
|
||||
pixWidth, pixHeight, GL_ALPHA, GL_UNSIGNED_BYTE,
|
||||
pixBuffer);
|
||||
|
||||
/* A mipmap is built only if a display list is currently building
|
||||
* otherwise it adds useless computations
|
||||
*/
|
||||
if (!(inContext->enableState.mipmap && inContext->enableState.glObjects))
|
||||
break;
|
||||
|
||||
level++; /* Next level of mipmap */
|
||||
pixWidth >>= 1;
|
||||
pixHeight >>= 1;
|
||||
} while ((pixWidth > minSize) && (pixHeight > minSize));
|
||||
|
||||
/* Finish to build the mipmap if necessary */
|
||||
if (inContext->enableState.mipmap && inContext->enableState.glObjects) {
|
||||
if (GLEW_VERSION_1_2 || GLEW_SGIS_texture_lod)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level - 1);
|
||||
else {
|
||||
/* The OpenGL driver does not support the extension GL_EXT_texture_lod
|
||||
* We must finish the pixmap until the mipmap level is 1x1.
|
||||
* Here the smaller mipmap levels will be transparent, no glyph will be
|
||||
* rendered.
|
||||
* TODO: Use gluScaleImage() to render the last levels.
|
||||
* Here we do not take the GL_ARB_pixel_buffer_object into account
|
||||
* because there are few chances that a gfx card that supports PBO, does
|
||||
* not support texture levels.
|
||||
*/
|
||||
assert(!GLEW_ARB_pixel_buffer_object);
|
||||
memset(pixBuffer, 0, pixWidth * pixHeight);
|
||||
while ((pixWidth > 0) || (pixHeight > 0)) {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, level, texX >> level, texY >> level,
|
||||
pixWidth ? pixWidth : 1,
|
||||
pixHeight ? pixHeight : 1, GL_ALPHA,
|
||||
GL_UNSIGNED_BYTE, pixBuffer);
|
||||
|
||||
level++;
|
||||
pixWidth >>= 1;
|
||||
pixHeight >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glPopClientAttrib();
|
||||
|
||||
if (pixBuffer)
|
||||
__glcFree(pixBuffer);
|
||||
|
||||
/* Add the new texture to the texture list and the new display list
|
||||
* to the list of display lists
|
||||
*/
|
||||
if (inContext->enableState.glObjects) {
|
||||
if (GLEW_ARB_vertex_buffer_object) {
|
||||
GLfloat* buffer = NULL;
|
||||
GLfloat* data = NULL;
|
||||
__GLCatlasElement* atlasNode = inGlyph->textureObject;
|
||||
|
||||
buffer = (GLfloat*)__glcMalloc(inContext->atlasWidth
|
||||
* inContext->atlasHeight * 20
|
||||
* sizeof(GLfloat));
|
||||
if (!buffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The display list ID is used as a flag to declare that the VBO has been
|
||||
* initialized and can be used.
|
||||
*/
|
||||
inGlyph->glObject[1] = 0xffffffff;
|
||||
|
||||
/* Here we do not use the GL command glBufferSubData() since it seems to
|
||||
* be buggy on some GL drivers (the DRI Intel specifically).
|
||||
* Instead, we use a workaround: the current values of the VBO are stored
|
||||
* in memory and new values are appended to them. Then, the content of
|
||||
* the resulting array replaces all the values previously stored in the
|
||||
* VBO.
|
||||
*/
|
||||
if (inContext->atlasCount > 1) {
|
||||
data = (GLfloat*)glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_READ_ONLY);
|
||||
if (!data) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(buffer);
|
||||
return;
|
||||
}
|
||||
memcpy(buffer, data, inContext->atlasCount * 20 * sizeof(GLfloat));
|
||||
glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
|
||||
}
|
||||
|
||||
data = buffer + atlasNode->position * 20;
|
||||
|
||||
data[0] = texX / texWidth;
|
||||
data[1] = texY / texHeight;
|
||||
data[2] = pixBoundingBox[0] / 64. / GLC_TEXTURE_SIZE;
|
||||
data[3] = pixBoundingBox[1] / 64. / GLC_TEXTURE_SIZE;
|
||||
data[4] = 0.f;
|
||||
data[5] = (texX + GLC_TEXTURE_SIZE - 1) / texWidth;
|
||||
data[6] = data[1];
|
||||
data[7] = pixBoundingBox[2] / 64. / GLC_TEXTURE_SIZE;
|
||||
data[8] = data[3];
|
||||
data[9] = 0.f;
|
||||
data[10] = data[5];
|
||||
data[11] = (texY + GLC_TEXTURE_SIZE - 1) / texHeight;
|
||||
data[12] = data[7];
|
||||
data[13] = pixBoundingBox[3] / 64. / GLC_TEXTURE_SIZE;
|
||||
data[14] = 0.f;
|
||||
data[15] = data[0];
|
||||
data[16] = data[11];
|
||||
data[17] = data[2];
|
||||
data[18] = data[13];
|
||||
data[19] = 0.f;
|
||||
|
||||
/* Size of the buffer data is equal to the number of glyphes than can be
|
||||
* stored in the texture times 20 GLfloat (4 vertices made of 3D
|
||||
* coordinates plus 2D texture coordinates : 4 * (3 + 2) = 20)
|
||||
*/
|
||||
glBufferDataARB(GL_ARRAY_BUFFER_ARB,
|
||||
inContext->atlasWidth * inContext->atlasHeight
|
||||
* 20 * sizeof(GLfloat), buffer, GL_STATIC_DRAW_ARB);
|
||||
|
||||
__glcFree(buffer);
|
||||
|
||||
/* Do the actual GL rendering */
|
||||
glInterleavedArrays(GL_T2F_V3F, 0, NULL);
|
||||
glDrawArrays(GL_QUADS, atlasNode->position * 4, 4);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
inGlyph->glObject[1] = glGenLists(1);
|
||||
if (!inGlyph->glObject[1]) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the display list */
|
||||
glNewList(inGlyph->glObject[1], GL_COMPILE);
|
||||
glScalef(1. / (64. * inScaleX), 1. / (64. * inScaleY) , 1.);
|
||||
|
||||
/* Modify the bouding box dimensions to compensate the glScalef() */
|
||||
pixBoundingBox[0] *= inScaleX / GLC_TEXTURE_SIZE;
|
||||
pixBoundingBox[1] *= inScaleY / GLC_TEXTURE_SIZE;
|
||||
pixBoundingBox[2] *= inScaleX / GLC_TEXTURE_SIZE;
|
||||
pixBoundingBox[3] *= inScaleY / GLC_TEXTURE_SIZE;
|
||||
|
||||
pixWidth = GLC_TEXTURE_SIZE;
|
||||
pixHeight = GLC_TEXTURE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do the actual GL rendering */
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(texX / texWidth, texY / texHeight);
|
||||
glVertex2iv(pixBoundingBox);
|
||||
glTexCoord2f((texX + pixWidth - 1) / texWidth, texY / texHeight);
|
||||
glVertex2i(pixBoundingBox[2], pixBoundingBox[1]);
|
||||
glTexCoord2f((texX + pixWidth - 1) / texWidth,
|
||||
(texY + pixHeight - 1) / texHeight);
|
||||
glVertex2iv(pixBoundingBox + 2);
|
||||
glTexCoord2f(texX / texWidth, (texY + pixHeight - 1) / texHeight);
|
||||
glVertex2i(pixBoundingBox[0], pixBoundingBox[3]);
|
||||
glEnd();
|
||||
|
||||
if (inContext->enableState.glObjects) {
|
||||
/* Finish display list creation */
|
||||
glScalef(64. * inScaleX, 64. * inScaleY, 1.);
|
||||
glEndList();
|
||||
glCallList(inGlyph->glObject[1]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2008, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* header of the routines used to render characters with textures.
|
||||
*/
|
||||
|
||||
#ifndef __glc_texture_h
|
||||
#define __glc_texture_h
|
||||
|
||||
#include "ofont.h"
|
||||
|
||||
#define GLC_TEXTURE_SIZE 64
|
||||
|
||||
struct __GLCatlasElementRec {
|
||||
FT_ListNodeRec node;
|
||||
|
||||
int position;
|
||||
__GLCglyph* glyph;
|
||||
};
|
||||
|
||||
void __glcReleaseAtlasElement(__GLCatlasElement* This, __GLCcontext* inContext);
|
||||
void __glcRenderCharTexture(const __GLCfont* inFont, __GLCcontext* inContext,
|
||||
const GLfloat inScaleX, const GLfloat inScaleY,
|
||||
__GLCglyph* inGlyph);
|
||||
#endif
|
|
@ -0,0 +1,292 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the so-called "Transformation commands" described in chapter 3.9
|
||||
* of the GLC specs.
|
||||
*/
|
||||
|
||||
/** \defgroup transform Transformation commands
|
||||
* The GLC transformation commands modify the value of \b GLC_BITMAP_MATRIX.
|
||||
* Glyph coordinates are defined in the em coordinate system. When the value
|
||||
* of \b GLC_RENDER_STYLE is \b GLC_BITMAP, GLC uses the 2x2
|
||||
* \b GLC_BITMAP_MATRIX to transform layouts from the em coordinate system to
|
||||
* the GL raster coordinate system in which bitmaps are drawn.
|
||||
*
|
||||
* When the value of the variable \b GLC_RENDER_STYLE is not \b GLC_BITMAP,
|
||||
* GLC performs no transformations on glyph coordinates. In this case, GLC
|
||||
* uses em coordinates directly as GL world coordinates when drawing a layout,
|
||||
* and it is the responsibility of the GLC client to issue GL commands that
|
||||
* set up the appropriate GL transformations.
|
||||
*
|
||||
* There is a stack of matrices for \b GLC_BITMAP_MATRIX, the stack depth is
|
||||
* at least 32 (that is, there is a stack of at least 32 matrices). Matrices
|
||||
* can be pushed or popped in the stack with glcPushMatrixQSO() and
|
||||
* glcPopMatrixQSO(). The maximum depth is implementation specific but can be
|
||||
* retrieved by calling glcGeti() with \b GLC_MAX_MATRIX_STACK_DEPTH_QSO. The
|
||||
* number of matrices that are currently stored in the stack can be retrieved
|
||||
* by calling glcGeti() with \b GLC_MATRIX_STACK_DEPTH_QSO.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "internal.h"
|
||||
|
||||
#define GLC_PI 3.1415926535
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command assigns the value [1 0 0 1] to the floating point vector
|
||||
* variable \b GLC_BITMAP_MATRIX.
|
||||
* \sa glcGetfv() with argument \b GLC_BITMAP_MATRIX
|
||||
* \sa glcLoadMatrix()
|
||||
* \sa glcMultMatrix()
|
||||
* \sa glcRotate()
|
||||
* \sa glcScale()
|
||||
*/
|
||||
void APIENTRY glcLoadIdentity(void)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->bitmapMatrix[0] = 1.;
|
||||
ctx->bitmapMatrix[1] = 0.;
|
||||
ctx->bitmapMatrix[2] = 0.;
|
||||
ctx->bitmapMatrix[3] = 1.;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command assigns the value [inMatrix[0] inMatrix[1] inMatrix[2]
|
||||
* inMatrix[3]] to the floating point vector variable \b GLC_BITMAP_MATRIX.
|
||||
*
|
||||
* \param inMatrix The value to assign to \b GLC_BITMAP_MATRIX
|
||||
* \sa glcGetfv() with argument \b GLC_BITMAP_MATRIX
|
||||
* \sa glcLoadIdentity()
|
||||
* \sa glcMultMatrix()
|
||||
* \sa glcRotate()
|
||||
* \sa glcScale()
|
||||
*/
|
||||
void APIENTRY glcLoadMatrix(const GLfloat *inMatrix)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
assert(inMatrix);
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(ctx->bitmapMatrix, inMatrix, 4 * sizeof(GLfloat));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command multiply the floating point vector variable
|
||||
* \b GLC_BITMAP_MATRIX by the incoming matrix \e inMatrix.
|
||||
*
|
||||
* \param inMatrix A pointer to a 2x2 matrix stored in column-major order
|
||||
* as 4 consecutives values.
|
||||
* \sa glcGetfv() with argument \b GLC_BITMAP_MATRIX
|
||||
* \sa glcLoadIdentity()
|
||||
* \sa glcLoadMatrix()
|
||||
* \sa glcRotate()
|
||||
* \sa glcScale()
|
||||
*/
|
||||
void APIENTRY glcMultMatrix(const GLfloat *inMatrix)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
GLfloat tempMatrix[4];
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
assert(inMatrix);
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(tempMatrix, ctx->bitmapMatrix, 4 * sizeof(GLfloat));
|
||||
|
||||
ctx->bitmapMatrix[0] = tempMatrix[0] * inMatrix[0]
|
||||
+ tempMatrix[2] * inMatrix[1];
|
||||
ctx->bitmapMatrix[1] = tempMatrix[1] * inMatrix[0]
|
||||
+ tempMatrix[3] * inMatrix[1];
|
||||
ctx->bitmapMatrix[2] = tempMatrix[0] * inMatrix[2]
|
||||
+ tempMatrix[2] * inMatrix[3];
|
||||
ctx->bitmapMatrix[3] = tempMatrix[1] * inMatrix[2]
|
||||
+ tempMatrix[3] * inMatrix[3];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command assigns the value [a b c d] to the floating point vector
|
||||
* variable \b GLC_BITMAP_MATRIX, where \e inAngle is measured in degrees,
|
||||
* \f$ \theta = inAngle * \pi / 180 \f$ and \n
|
||||
* \f$ \left [ \begin {array}{ll} a & b \\ c & d \\ \end {array} \right ]
|
||||
* = \left [ \begin {array}{ll} GLC\_BITMAP\_MATRIX[0] & GLC\_BITMAP\_MATRIX[2]
|
||||
* \\ GLC\_BITMAP\_MATRIX[1] & GLC\_BITMAP\_MATRIX[3] \\ \end{array}
|
||||
* \right ]
|
||||
* \left [ \begin {array}{ll} cos \theta & sin \theta \\
|
||||
* -sin \theta & cos\theta \\ \end{array} \right ]
|
||||
* \f$
|
||||
* \param inAngle The angle of rotation around the Z axis, in degrees.
|
||||
* \sa glcGetfv() with argument \b GLC_BITMAP_MATRIX
|
||||
* \sa glcLoadIdentity()
|
||||
* \sa glcLoadMatrix()
|
||||
* \sa glcMultMatrix()
|
||||
* \sa glcScale()
|
||||
*/
|
||||
void APIENTRY glcRotate(GLfloat inAngle)
|
||||
{
|
||||
GLfloat tempMatrix[4];
|
||||
GLfloat radian = inAngle * GLC_PI / 180.;
|
||||
GLfloat sine = sin(radian);
|
||||
GLfloat cosine = cos(radian);
|
||||
|
||||
tempMatrix[0] = cosine;
|
||||
tempMatrix[1] = sine;
|
||||
tempMatrix[2] = -sine;
|
||||
tempMatrix[3] = cosine;
|
||||
|
||||
glcMultMatrix(tempMatrix);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command produces a general scaling along the \b x and \b y
|
||||
* axes, that is, it assigns the value [a b c d] to the floating point
|
||||
* vector variable \b GLC_BITMAP_MATRIX, where \n
|
||||
* \f$ \left [ \begin {array}{ll} a & b \\ c & d \\ \end {array} \right ]
|
||||
* = \left [ \begin {array}{ll} GLC\_BITMAP\_MATRIX[0] & GLC\_BITMAP\_MATRIX[2]
|
||||
* \\ GLC\_BITMAP\_MATRIX[1] & GLC\_BITMAP\_MATRIX[3] \\ \end{array}
|
||||
* \right ]
|
||||
* \left [ \begin {array}{ll} inX & 0 \\
|
||||
* 0 & inY \\ \end{array} \right ]
|
||||
* \f$
|
||||
* \param inX The scale factor along the \b x axis
|
||||
* \param inY The scale factor along the \b y axis
|
||||
* \sa glcGetfv() with argument \b GLC_BITMAP_MATRIX
|
||||
* \sa glcLoadIdentity()
|
||||
* \sa glcLoadMatrix()
|
||||
* \sa glcMultMatrix()
|
||||
* \sa glcRotate()
|
||||
*/
|
||||
void APIENTRY glcScale(GLfloat inX, GLfloat inY)
|
||||
{
|
||||
GLfloat tempMatrix[4];
|
||||
|
||||
tempMatrix[0] = inX;
|
||||
tempMatrix[1] = 0.;
|
||||
tempMatrix[2] = 0.;
|
||||
tempMatrix[3] = inY;
|
||||
|
||||
glcMultMatrix(tempMatrix);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command pushes the stack down by one, duplicating the current
|
||||
* \b GLC_BITMAP_MATRIX in both the top of the stack and the entry below it.
|
||||
* Pushing a matrix onto a full stack generates the error
|
||||
* \b GLC_STACK_OVERFLOW_QSO.
|
||||
* \sa glcPopMatrixQSO()
|
||||
* \sa glcGeti() with argument \b GLC_MATRIX_STACK_DEPTH_QSO
|
||||
* \sa glcGeti() with argument \b GLC_MAX_MATRIX_STACK_DEPTH_QSO
|
||||
*/
|
||||
void APIENTRY glcPushMatrixQSO(void)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->bitmapMatrixStackDepth >= GLC_MAX_MATRIX_STACK_DEPTH) {
|
||||
__glcRaiseError(GLC_STACK_OVERFLOW_QSO);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(ctx->bitmapMatrix+4, ctx->bitmapMatrix, 4*sizeof(GLfloat));
|
||||
ctx->bitmapMatrix += 4;
|
||||
ctx->bitmapMatrixStackDepth++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** \ingroup transform
|
||||
* This command pops the top entry off the stack, replacing the current
|
||||
* \b GLC_BITMAP_MATRIX with the matrix that was the second entry in the
|
||||
* stack.
|
||||
* Popping a matrix off a stack with only one entry generates the error
|
||||
* \b GLC_STACK_OVERFLOW_QSO.
|
||||
* \sa glcPushMatrixQSO()
|
||||
* \sa glcGeti() with argument \b GLC_MATRIX_STACK_DEPTH_QSO
|
||||
* \sa glcGeti() with argument \b GLC_MAX_MATRIX_STACK_DEPTH_QSO
|
||||
*/
|
||||
void APIENTRY glcPopMatrixQSO(void)
|
||||
{
|
||||
__GLCcontext *ctx = NULL;
|
||||
|
||||
GLC_INIT_THREAD();
|
||||
|
||||
/* Check if the current thread owns a context state */
|
||||
ctx = GLC_GET_CURRENT_CONTEXT();
|
||||
if (!ctx) {
|
||||
__glcRaiseError(GLC_STATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->bitmapMatrixStackDepth <= 1) {
|
||||
__glcRaiseError(GLC_STACK_UNDERFLOW_QSO);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->bitmapMatrix -= 4;
|
||||
ctx->bitmapMatrixStackDepth--;
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,782 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2009, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/* This file defines miscellaneous utility routines used for Unicode management
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* defines the routines used to manipulate Unicode strings and characters
|
||||
*/
|
||||
|
||||
#include <fribidi/fribidi.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
|
||||
/* Find a Unicode name from its code */
|
||||
const GLCchar8* __glcNameFromCode(const GLint code)
|
||||
{
|
||||
GLint position = -1;
|
||||
|
||||
if ((code < 0) || (code > __glcMaxCode)) {
|
||||
static char buffer[20];
|
||||
|
||||
if (code > 0x10ffff) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(buffer, 20, "Character 0x%x", code);
|
||||
return (const GLCchar8*)buffer;
|
||||
}
|
||||
|
||||
position = __glcNameFromCodeArray[code];
|
||||
if (position == -1) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const GLCchar8*)__glcCodeFromNameArray[position].name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Find a Unicode code from its name */
|
||||
GLint __glcCodeFromName(const GLCchar8* name)
|
||||
{
|
||||
int start = 0;
|
||||
int end = __glcCodeFromNameSize;
|
||||
int middle = (end + start) / 2;
|
||||
int res = 0;
|
||||
|
||||
while (end - start > 1) {
|
||||
res = strcmp((const char*)name, __glcCodeFromNameArray[middle].name);
|
||||
if (res > 0)
|
||||
start = middle;
|
||||
else if (res < 0)
|
||||
end = middle;
|
||||
else
|
||||
return __glcCodeFromNameArray[middle].code;
|
||||
middle = (end + start) / 2;
|
||||
}
|
||||
if (strcmp((const char*)name, __glcCodeFromNameArray[start].name) == 0)
|
||||
return __glcCodeFromNameArray[start].code;
|
||||
if (strcmp((const char*)name, __glcCodeFromNameArray[end].name) == 0)
|
||||
return __glcCodeFromNameArray[end].code;
|
||||
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a character from UCS1 to UTF-8 and return the number of bytes
|
||||
* needed to encode the char.
|
||||
*/
|
||||
static int __glcUcs1ToUtf8(const GLCchar8 ucs1, GLCchar8 dest[FC_UTF8_MAX_LEN])
|
||||
{
|
||||
GLCchar8 *d = dest;
|
||||
|
||||
if (ucs1 < 0x80)
|
||||
*d++ = ucs1;
|
||||
else {
|
||||
*d++ = ((ucs1 >> 6) & 0x1F) | 0xC0;
|
||||
*d++ = (ucs1 & 0x3F) | 0x80;
|
||||
}
|
||||
|
||||
return d - dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a character from UCS2 to UTF-8 and return the number of bytes
|
||||
* needed to encode the char.
|
||||
*/
|
||||
static int __glcUcs2ToUtf8(const GLCchar16 ucs2, GLCchar8 dest[FC_UTF8_MAX_LEN])
|
||||
{
|
||||
GLCchar8 *d = dest;
|
||||
|
||||
if (ucs2 < 0x80)
|
||||
*d++ = ucs2;
|
||||
else if (ucs2 < 0x800) {
|
||||
*d++ = ((ucs2 >> 6) & 0x1F) | 0xC0;
|
||||
*d++ = (ucs2 & 0x3F) | 0x80;
|
||||
}
|
||||
else {
|
||||
*d++ = ((ucs2 >> 12) & 0x0F) | 0xE0;
|
||||
*d++ = ((ucs2 >> 6) & 0x3F) | 0x80;
|
||||
*d++ = (ucs2 & 0x3F) | 0x80;
|
||||
}
|
||||
|
||||
return d - dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a character from UTF-8 to UCS1 and return the number of bytes
|
||||
* needed to encode the character.
|
||||
* According to the GLC specs, when the value of a character code exceed the
|
||||
* range of the character encoding, the returned character is converted
|
||||
* to a character sequence \<hexcode> where 'hexcode' is the original
|
||||
* character code represented as a sequence of hexadecimal digits
|
||||
*/
|
||||
static int __glcUtf8ToUcs1(const GLCchar8* src_orig,
|
||||
GLCchar8 dst[GLC_OUT_OF_RANGE_LEN], int len,
|
||||
int* dstlen)
|
||||
{
|
||||
GLCchar32 result = 0;
|
||||
int src_shift = FcUtf8ToUcs4(src_orig, &result, len);
|
||||
|
||||
if (src_shift > 0) {
|
||||
/* src_orig is a well-formed UTF-8 character */
|
||||
if (result < 0x100) {
|
||||
*dst = result;
|
||||
*dstlen = 1;
|
||||
}
|
||||
else {
|
||||
/* Convert to the string '\<xxx>' */
|
||||
#ifdef _MSC_VER
|
||||
*dstlen = sprintf_s((char*)dst, GLC_OUT_OF_RANGE_LEN, "\\<%X>", result);
|
||||
/* sprintf_s returns -1 on any error, and the number of characters
|
||||
* written to the string not including the terminating null otherwise.
|
||||
* Insufficient length of the destination buffer is an error and the
|
||||
* buffer is set to an empty string. */
|
||||
if (*dstlen < 0)
|
||||
*dstlen = 0;
|
||||
#else
|
||||
*dstlen = snprintf((char*)dst, GLC_OUT_OF_RANGE_LEN, "\\<%X>", result);
|
||||
/* Standard ISO/IEC 9899:1999 (ISO C99) snprintf, which it appears
|
||||
* Microsoft has not implemented for their operating systems. Return
|
||||
* value is length of the string that would have been written into
|
||||
* the destination buffer not including the terminating null had their
|
||||
* been enough space. Truncation has occurred if return value is >=
|
||||
* destination buffer size. */
|
||||
if (*dstlen >= GLC_OUT_OF_RANGE_LEN)
|
||||
*dstlen = GLC_OUT_OF_RANGE_LEN - 1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return src_shift;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a character from UTF-8 to UCS1 and return the number of bytes
|
||||
* needed to encode the character.
|
||||
* According to the GLC specs, when the value of a character code exceed the
|
||||
* range of the character encoding, the returned character is converted
|
||||
* to a character sequence \<hexcode> where 'hexcode' is the original
|
||||
* character code represented as a sequence of hexadecimal digits
|
||||
*/
|
||||
static int __glcUtf8ToUcs2(const GLCchar8* src_orig,
|
||||
GLCchar16 dst[GLC_OUT_OF_RANGE_LEN], int len,
|
||||
int* dstlen)
|
||||
{
|
||||
GLCchar32 result = 0;
|
||||
int src_shift = FcUtf8ToUcs4(src_orig, &result, len);
|
||||
|
||||
if (src_shift > 0) {
|
||||
/* src_orig is a well-formed UTF-8 character */
|
||||
if (result < 0x10000) {
|
||||
*dst = result;
|
||||
*dstlen = 1;
|
||||
}
|
||||
else {
|
||||
/* Convert to the string '\<xxx>' */
|
||||
int count;
|
||||
char* src = NULL;
|
||||
char buffer[GLC_OUT_OF_RANGE_LEN];
|
||||
|
||||
#ifdef _MSC_VER
|
||||
*dstlen = sprintf_s((char*)buffer, GLC_OUT_OF_RANGE_LEN, "\\<%X>",
|
||||
result);
|
||||
/* sprintf_s returns -1 on any error, and the number of characters
|
||||
* written to the string not including the terminating null otherwise.
|
||||
* Insufficient length of the destination buffer is an error and the
|
||||
* buffer is set to an empty string. */
|
||||
if (*dstlen < 0)
|
||||
*dstlen = 0;
|
||||
#else
|
||||
*dstlen = snprintf((char*)buffer, GLC_OUT_OF_RANGE_LEN, "\\<%X>", result);
|
||||
/* Standard ISO/IEC 9899:1999 (ISO C99) snprintf, which it appears
|
||||
* Microsoft has not implemented for their operating systems. Return
|
||||
* value is length of the string that would have been written into
|
||||
* the destination buffer not including the terminating null had their
|
||||
* been enough space. Truncation has occurred if return value is >=
|
||||
* destination buffer size. */
|
||||
if (*dstlen >= GLC_OUT_OF_RANGE_LEN)
|
||||
*dstlen = GLC_OUT_OF_RANGE_LEN - 1;
|
||||
#endif
|
||||
for (count = 0, src = buffer; count < *dstlen; count++, *dst++ = *src++);
|
||||
*dst = 0; /* Terminating '\0' character */
|
||||
}
|
||||
}
|
||||
return src_shift;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert 'inString' in the UTF-8 format and return a copy of the converted
|
||||
* string.
|
||||
*/
|
||||
GLCchar8* __glcConvertToUtf8(const GLCchar* inString, const GLint inStringType)
|
||||
{
|
||||
GLCchar8 buffer[FC_UTF8_MAX_LEN > 8 ? FC_UTF8_MAX_LEN : 8];
|
||||
GLCchar8* string = NULL;
|
||||
GLCchar8* ptr = NULL;
|
||||
int len;
|
||||
|
||||
switch(inStringType) {
|
||||
case GLC_UCS1:
|
||||
{
|
||||
const GLCchar8* ucs1 = NULL;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
for (len = 0, ucs1 = (const GLCchar8*)inString; *ucs1;
|
||||
len += __glcUcs1ToUtf8(*ucs1++, buffer));
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar8*)__glcMalloc((len+1)*sizeof(GLCchar8));
|
||||
if (!string) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Perform the conversion */
|
||||
for (ucs1 = (const GLCchar8*)inString, ptr = string; *ucs1;
|
||||
ptr += __glcUcs1ToUtf8(*ucs1++, ptr));
|
||||
*ptr = 0;
|
||||
}
|
||||
break;
|
||||
case GLC_UCS2:
|
||||
{
|
||||
const GLCchar16* ucs2 = NULL;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
for (len = 0, ucs2 = (const GLCchar16*)inString; *ucs2;
|
||||
len += __glcUcs2ToUtf8(*ucs2++, buffer));
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar8*)__glcMalloc((len+1)*sizeof(GLCchar8));
|
||||
if (!string) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Perform the conversion */
|
||||
for (ucs2 = (const GLCchar16*)inString, ptr = string; *ucs2;
|
||||
ptr += __glcUcs2ToUtf8(*ucs2++, ptr));
|
||||
*ptr = 0;
|
||||
}
|
||||
break;
|
||||
case GLC_UCS4:
|
||||
{
|
||||
const GLCchar32* ucs4 = NULL;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
for (len = 0, ucs4 = (const GLCchar32*)inString; *ucs4;
|
||||
len += FcUcs4ToUtf8(*ucs4++, buffer));
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar8*)__glcMalloc((len+1)*sizeof(GLCchar8));
|
||||
if (!string) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Perform the conversion */
|
||||
for (ucs4 = (const GLCchar32*)inString, ptr = string; *ucs4;
|
||||
ptr += FcUcs4ToUtf8(*ucs4++, ptr));
|
||||
*ptr = 0;
|
||||
}
|
||||
break;
|
||||
case GLC_UTF8_QSO:
|
||||
/* If the string is already encoded in UTF-8 format then all we need to do
|
||||
* is to make a copy of it.
|
||||
*/
|
||||
#ifdef __WIN32__
|
||||
string = (GLCchar8*)_strdup((const char*)inString);
|
||||
#else
|
||||
string = (GLCchar8*)strdup((const char*)inString);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert 'inString' from the UTF-8 format and return a copy of the
|
||||
* converted string in the context buffer.
|
||||
*/
|
||||
GLCchar* __glcConvertFromUtf8ToBuffer(__GLCcontext* This,
|
||||
const GLCchar8* inString)
|
||||
{
|
||||
GLCchar* string = NULL;
|
||||
const GLCchar8* utf8 = NULL;
|
||||
int len_buffer = 0;
|
||||
int len = 0;
|
||||
int shift = 0;
|
||||
|
||||
assert(inString);
|
||||
|
||||
switch(This->stringState.stringType) {
|
||||
case GLC_UCS1:
|
||||
{
|
||||
GLCchar8 buffer[GLC_OUT_OF_RANGE_LEN];
|
||||
GLCchar8* ucs1 = NULL;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
utf8 = inString;
|
||||
while(*utf8) {
|
||||
shift = __glcUtf8ToUcs1(utf8, buffer, strlen((const char*)utf8),
|
||||
&len_buffer);
|
||||
if (shift < 0) {
|
||||
/* There is an ill-formed character in the UTF-8 string, abort */
|
||||
return NULL;
|
||||
}
|
||||
utf8 += shift;
|
||||
len += len_buffer;
|
||||
}
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar*)__glcContextQueryBuffer(This,
|
||||
(len+1)*sizeof(GLCchar8));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
/* Perform the conversion */
|
||||
ucs1 = (GLCchar8*)string;
|
||||
utf8 = inString;
|
||||
while(*utf8) {
|
||||
utf8 += __glcUtf8ToUcs1(utf8, ucs1, strlen((const char*)utf8),
|
||||
&len_buffer);
|
||||
ucs1 += len_buffer;
|
||||
}
|
||||
|
||||
*ucs1 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UCS2:
|
||||
{
|
||||
GLCchar16 buffer[GLC_OUT_OF_RANGE_LEN];
|
||||
GLCchar16* ucs2 = NULL;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
utf8 = inString;
|
||||
while(*utf8) {
|
||||
shift = __glcUtf8ToUcs2(utf8, buffer, strlen((const char*)utf8),
|
||||
&len_buffer);
|
||||
if (shift < 0) {
|
||||
/* There is an ill-formed character in the UTF-8 string, abort */
|
||||
return NULL;
|
||||
}
|
||||
utf8 += shift;
|
||||
len += len_buffer;
|
||||
}
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar*)__glcContextQueryBuffer(This,
|
||||
(len+1)*sizeof(GLCchar16));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
/* Perform the conversion */
|
||||
ucs2 = (GLCchar16*)string;
|
||||
utf8 = inString;
|
||||
while(*utf8) {
|
||||
utf8 += __glcUtf8ToUcs2(utf8, ucs2, strlen((const char*)utf8),
|
||||
&len_buffer);
|
||||
ucs2 += len_buffer;
|
||||
}
|
||||
*ucs2 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UCS4:
|
||||
{
|
||||
GLCchar32 buffer = 0;
|
||||
GLCchar32* ucs4 = NULL;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
utf8 = inString;
|
||||
while(*utf8) {
|
||||
shift = FcUtf8ToUcs4(utf8, &buffer, strlen((const char*)utf8));
|
||||
if (shift < 0) {
|
||||
/* There is an ill-formed character in the UTF-8 string, abort */
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
utf8 += shift;
|
||||
len++;
|
||||
}
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar*)__glcContextQueryBuffer(This,
|
||||
(len+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
|
||||
/* Perform the conversion */
|
||||
utf8 = inString;
|
||||
ucs4 = (GLCchar32*)string;
|
||||
while(*utf8)
|
||||
utf8 += FcUtf8ToUcs4(utf8, ucs4++, strlen((const char*)utf8));
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UTF8_QSO:
|
||||
/* If the string is already encoded in UTF-8 format then all we need to do
|
||||
* is to make a copy of it.
|
||||
*/
|
||||
string = (GLCchar*)__glcContextQueryBuffer(This,
|
||||
strlen((const char*)inString)+1);
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
strcpy((char*)string, (const char*)inString);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a UCS-4 character code into the current string type. The result is
|
||||
* stored in a GLint. If the code can not be converted to the current string
|
||||
* type a GLC_PARAMETER_ERROR is issued.
|
||||
*/
|
||||
GLint __glcConvertUcs4ToGLint(__GLCcontext *inContext, GLint inCode)
|
||||
{
|
||||
switch(inContext->stringState.stringType) {
|
||||
case GLC_UCS2:
|
||||
/* Check that inCode can be stored in UCS-2 format */
|
||||
if (inCode <= 65535)
|
||||
break;
|
||||
case GLC_UCS1:
|
||||
/* Check that inCode can be stored in UCS-1 format */
|
||||
if (inCode <= 255)
|
||||
break;
|
||||
case GLC_UTF8_QSO:
|
||||
/* A Unicode codepoint can be no higher than 0x10ffff
|
||||
* (see Unicode specs)
|
||||
*/
|
||||
if (inCode > 0x10ffff) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
/* Codepoints lower or equal to 0x10ffff can be encoded on 4 bytes in
|
||||
* UTF-8 format
|
||||
*/
|
||||
GLCchar8 buffer[FC_UTF8_MAX_LEN > 8 ? FC_UTF8_MAX_LEN : 8];
|
||||
#ifndef NDEBUG
|
||||
int len = FcUcs4ToUtf8((GLCchar32)inCode, buffer);
|
||||
assert((size_t)len <= sizeof(GLint));
|
||||
#else
|
||||
FcUcs4ToUtf8((GLCchar32)inCode, buffer);
|
||||
#endif
|
||||
|
||||
return *((GLint*)buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return inCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a character encoded in the current string type to the UCS-4 format.
|
||||
* This function is needed since the GLC specs store individual character codes
|
||||
* in GLint which may cause problems for the UTF-8 format.
|
||||
*/
|
||||
GLint __glcConvertGLintToUcs4(const __GLCcontext *inContext, GLint inCode)
|
||||
{
|
||||
GLint code = inCode;
|
||||
|
||||
if (inCode < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (inContext->stringState.stringType) {
|
||||
case GLC_UCS1:
|
||||
if (inCode > 0xff) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case GLC_UCS2:
|
||||
if (inCode > 0xffff) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case GLC_UTF8_QSO:
|
||||
/* Convert the codepoint in UCS4 format and check if it is ill-formed or
|
||||
* not
|
||||
*/
|
||||
if (FcUtf8ToUcs4((GLCchar8*)&inCode, (GLCchar32*)&code,
|
||||
sizeof(GLint)) < 0) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert 'inString' (stored in logical order) to UCS4 format and return a
|
||||
* copy of the converted string in visual order.
|
||||
*/
|
||||
GLCchar32* __glcConvertToVisualUcs4(__GLCcontext* inContext,
|
||||
GLboolean *outIsRTL, GLint *outLength,
|
||||
const GLCchar* inString)
|
||||
{
|
||||
GLCchar32* string = NULL;
|
||||
int length = 0;
|
||||
FriBidiCharType base = FRIBIDI_TYPE_ON;
|
||||
GLCchar32* visualString = NULL;
|
||||
|
||||
assert(inString);
|
||||
|
||||
switch(inContext->stringState.stringType) {
|
||||
case GLC_UCS1:
|
||||
{
|
||||
const GLCchar8* ucs1 = (const GLCchar8*)inString;
|
||||
GLCchar32* ucs4 = NULL;
|
||||
|
||||
length = strlen((const char*)ucs1);
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(length+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
for (ucs4 = string; *ucs1; ucs1++, ucs4++)
|
||||
*ucs4 = (GLCchar32)(*ucs1);
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UCS2:
|
||||
{
|
||||
const GLCchar16* ucs2 = NULL;
|
||||
GLCchar32* ucs4 = NULL;
|
||||
|
||||
for (ucs2 = (const GLCchar16*)inString; *ucs2; ucs2++, length++);
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(length+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
for (ucs2 = (const GLCchar16*)inString, ucs4 = string; *ucs2;
|
||||
ucs2++, ucs4++)
|
||||
*ucs4 = (GLCchar32)(*ucs2);
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UCS4:
|
||||
{
|
||||
const GLCchar32* ucs4 = NULL;
|
||||
|
||||
for (ucs4 = (const GLCchar32*)inString; *ucs4; ucs4++, length++);
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(length+1)*sizeof(int));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
memcpy(string, inString, length*sizeof(int));
|
||||
|
||||
((int*)string)[length] = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UTF8_QSO:
|
||||
{
|
||||
GLCchar32* ucs4 = NULL;
|
||||
const GLCchar8* utf8 = NULL;
|
||||
GLCchar32 buffer = 0;
|
||||
int shift = 0;
|
||||
|
||||
/* Determine the length of the final string */
|
||||
utf8 = (const GLCchar8*)inString;
|
||||
while(*utf8) {
|
||||
shift = FcUtf8ToUcs4(utf8, &buffer, strlen((const char*)utf8));
|
||||
if (shift < 0) {
|
||||
/* There is an ill-formed character in the UTF-8 string, abort */
|
||||
return NULL;
|
||||
}
|
||||
utf8 += shift;
|
||||
length++;
|
||||
}
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(length+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
/* Perform the conversion */
|
||||
utf8 = (const GLCchar8*)inString;
|
||||
ucs4 = (GLCchar32*)string;
|
||||
while(*utf8)
|
||||
utf8 += FcUtf8ToUcs4(utf8, ucs4++, strlen((const char*)utf8));
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (length) {
|
||||
visualString = string + length + 1;
|
||||
if (!fribidi_log2vis(string, length, &base, visualString, NULL, NULL,
|
||||
NULL)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*outIsRTL = FRIBIDI_IS_RTL(base) ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
else
|
||||
visualString = string;
|
||||
|
||||
*outLength = length;
|
||||
|
||||
return visualString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert 'inCount' characters of 'inString' (stored in logical order) to UCS4
|
||||
* format and return a copy of the converted string in visual order.
|
||||
*/
|
||||
GLCchar32* __glcConvertCountedStringToVisualUcs4(__GLCcontext* inContext,
|
||||
GLboolean *outIsRTL,
|
||||
const GLCchar* inString,
|
||||
const GLint inCount)
|
||||
{
|
||||
GLCchar32* string = NULL;
|
||||
FriBidiCharType base = FRIBIDI_TYPE_ON;
|
||||
GLCchar32* visualString = NULL;
|
||||
|
||||
assert(inString);
|
||||
|
||||
switch(inContext->stringState.stringType) {
|
||||
case GLC_UCS1:
|
||||
{
|
||||
const GLCchar8* ucs1 = (const GLCchar8*)inString;
|
||||
GLCchar32* ucs4 = NULL;
|
||||
GLint i = 0;
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(inCount+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
ucs4 = string;
|
||||
|
||||
for (i = 0; i < inCount; i++)
|
||||
*(ucs4++) = (GLCchar32)(*(ucs1++));
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UCS2:
|
||||
{
|
||||
const GLCchar16* ucs2 = NULL;
|
||||
GLCchar32* ucs4 = NULL;
|
||||
GLint i = 0;
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(inCount+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
ucs2 = (const GLCchar16*)inString;
|
||||
ucs4 = string;
|
||||
|
||||
for (i = 0 ; i < inCount; i++)
|
||||
*(ucs4++) = (GLCchar32)(*(ucs2++));
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UCS4:
|
||||
{
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(inCount+1)*sizeof(int));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
memcpy(string, inString, inCount*sizeof(int));
|
||||
|
||||
((int*)string)[inCount] = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
case GLC_UTF8_QSO:
|
||||
{
|
||||
GLCchar32* ucs4 = NULL;
|
||||
const GLCchar8* utf8 = NULL;
|
||||
GLint i = 0;
|
||||
|
||||
/* Allocate the room to store the final string */
|
||||
string = (GLCchar32*)__glcContextQueryBuffer(inContext,
|
||||
2*(inCount+1)*sizeof(GLCchar32));
|
||||
if (!string)
|
||||
return NULL; /* GLC_RESOURCE_ERROR has been raised */
|
||||
|
||||
/* Perform the conversion */
|
||||
utf8 = (const GLCchar8*)inString;
|
||||
ucs4 = (GLCchar32*)string;
|
||||
|
||||
for (i = 0; i < inCount; i++)
|
||||
utf8 += FcUtf8ToUcs4(utf8, ucs4++, strlen((const char*)utf8));
|
||||
|
||||
*ucs4 = 0; /* Add the '\0' termination of the string */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
visualString = string + inCount;
|
||||
if (!fribidi_log2vis(string, inCount, &base, visualString, NULL, NULL,
|
||||
NULL)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*outIsRTL = FRIBIDI_IS_RTL(base) ? GL_TRUE : GL_FALSE;
|
||||
|
||||
return visualString;
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id: ocharmap.c,v 1.17 2007/02/25 13:30:48 bcoconni Exp $ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCcharMap which manage the charmaps of both the fonts
|
||||
* and the masters. One of the purpose of this object is to encapsulate the
|
||||
* GLYPHSET structure from Win32 GDI and to add it some more functionalities.
|
||||
* It also allows to centralize the character map management for easier
|
||||
* maintenance.
|
||||
*/
|
||||
|
||||
/* QuesoGLC needs Windows 2000 or newer */
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
* The user must give the initial GLYPHSET of the font or the master (which
|
||||
* may be NULL) in which case the character map will be empty.
|
||||
*/
|
||||
__GLCcharMap* __glcCharMapCreate(__GLCmaster* inMaster)
|
||||
{
|
||||
__GLCcharMap* This = NULL;
|
||||
|
||||
This = (__GLCcharMap*)__glcMalloc(sizeof(__GLCcharMap));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (inMaster) {
|
||||
HFONT font;
|
||||
DWORD size;
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcFree(This);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
font = CreateFontIndirect(&inMaster->pattern->elfLogFont);
|
||||
if (FAILED(font)) {
|
||||
DeleteDC(dc);
|
||||
__glcFree(This);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
if (FAILED(SelectObject(dc, font))) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcFree(This);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
size = GetFontUnicodeRanges(dc, NULL);
|
||||
if (!size) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcFree(This);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
This->charSet = (LPGLYPHSET)__glcMalloc(size);
|
||||
if (!This->charSet) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
if (!GetFontUnicodeRanges(dc, This->charSet)) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcFree(This->charSet);
|
||||
__glcFree(This);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
}
|
||||
else {
|
||||
This->charSet = (LPGLYPHSET)__glcMalloc(sizeof(GLYPHSET));
|
||||
if (!This->charSet) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* The array 'map' will contain the actual character map */
|
||||
This->map = __glcArrayCreate(sizeof(__GLCcharMapElement));
|
||||
if (!This->map) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This->charSet);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcCharMapDestroy(__GLCcharMap* This)
|
||||
{
|
||||
if (This->map)
|
||||
__glcArrayDestroy(This->map);
|
||||
|
||||
__glcFree(This->charSet);
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Add a given character to the character map. Afterwards, the character map
|
||||
* will associate the glyph 'inGlyph' to the Unicode codepoint 'inCode'.
|
||||
*/
|
||||
void __glcCharMapAddChar(__GLCcharMap* This, GLint inCode, __GLCglyph* inGlyph)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
__GLCcharMapElement* newElement = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to look for the place where to add the new
|
||||
* character.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
/* If the character map already contains the new character then update the
|
||||
* glyph then return.
|
||||
*/
|
||||
if (element[middle].mappedCode == (GLCulong)inCode) {
|
||||
element[middle].glyph = inGlyph;
|
||||
return;
|
||||
}
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* If we have reached the end of the array then updated the rank 'middle'
|
||||
* accordingly.
|
||||
*/
|
||||
if ((end >= 0) && (element[middle].mappedCode < (GLCulong)inCode))
|
||||
middle++;
|
||||
|
||||
/* Insert the new character in the character map */
|
||||
newElement = (__GLCcharMapElement*)__glcArrayInsertCell(This->map, middle, 1);
|
||||
if (!newElement)
|
||||
return;
|
||||
|
||||
newElement->mappedCode = inCode;
|
||||
newElement->glyph = inGlyph;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove a character from the character map */
|
||||
void __glcCharMapRemoveChar(__GLCcharMap* This, GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to look for the place where to add the new
|
||||
* character.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
/* When the character is found remove it from the array and return */
|
||||
if (element[middle].mappedCode == (GLCulong)inCode) {
|
||||
__glcArrayRemove(This->map, middle);
|
||||
break;
|
||||
}
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the Unicode character name of the character which codepoint is inCode.
|
||||
* Note : since the character maps of the fonts can be altered, this function
|
||||
* can return 'LATIN CAPITAL LETTER B' whereas inCode contained 65 (which is
|
||||
* the Unicode code point of 'LATIN CAPITAL LETTER A'.
|
||||
*/
|
||||
GLCchar* __glcCharMapGetCharName(__GLCcharMap* This, GLint inCode,
|
||||
__GLCcontext* inContext)
|
||||
{
|
||||
GLCchar *buffer = NULL;
|
||||
GLCchar8* name = NULL;
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to look for the Unicode codepoint that the
|
||||
* request character maps to.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
if (element[middle].mappedCode == (GLCulong)inCode) {
|
||||
inCode = element[middle].glyph->codepoint;
|
||||
break;
|
||||
}
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* If we have not found the character in the character map, it means that
|
||||
* the mapped code is equal to 'inCode' otherwise inCode is modified to
|
||||
* contain the mapped code.
|
||||
*/
|
||||
name = __glcNameFromCode(inCode);
|
||||
if (!name) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
/* Convert the Unicode to the current string type */
|
||||
buffer = __glcConvertFromUtf8ToBuffer(inContext, name,
|
||||
inContext->stringState.stringType);
|
||||
if (!buffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the glyph corresponding to codepoint 'inCode' */
|
||||
__GLCglyph* __glcCharMapGetGlyph(__GLCcharMap* This, GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to find the glyph of the requested
|
||||
* character.
|
||||
*/
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
if (element[middle].mappedCode == (GLCulong)inCode)
|
||||
/* When the character is found return the corresponding glyph */
|
||||
return element[middle].glyph;
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* No glyph has been defined yet for the requested character */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Check if a character is in the character map */
|
||||
GLboolean __glcCharMapHasChar(__GLCcharMap* This, GLint inCode)
|
||||
{
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int start = 0, middle = 0, end = 0;
|
||||
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
assert(inCode >= 0);
|
||||
|
||||
/* Characters are stored by ascending order of their mapped code */
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
|
||||
end = GLC_ARRAY_LENGTH(This->map) - 1;
|
||||
|
||||
/* Parse the array by dichotomy to find the requested character. */
|
||||
while (start <= end) {
|
||||
middle = (start + end) >> 1;
|
||||
/* The character has been found : return GL_TRUE */
|
||||
if (element[middle].mappedCode == (GLCulong)inCode)
|
||||
return GL_TRUE;
|
||||
else if (element[middle].mappedCode > (GLCulong)inCode)
|
||||
end = middle - 1;
|
||||
else
|
||||
start = middle + 1;
|
||||
}
|
||||
|
||||
/* Check if the character identified by inCode exists in the font */
|
||||
if (This->charSet->cGlyphsSupported) {
|
||||
DWORD i = 0;
|
||||
LPGLYPHSET charSet = This->charSet;
|
||||
LPWCRANGE range = charSet->ranges;
|
||||
|
||||
for (; i < charSet->cRanges; i++, range++) {
|
||||
if (inCode > (range->wcLow + range->cGlyphs - 1))
|
||||
continue;
|
||||
if (inCode >= range->wcLow)
|
||||
return GL_TRUE;
|
||||
}
|
||||
}
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the name of the character which is stored at rank 'inIndex' in the
|
||||
* GLYPHSET of the face.
|
||||
*/
|
||||
GLCchar* __glcCharMapGetCharNameByIndex(__GLCcharMap* This, GLint inIndex,
|
||||
__GLCcontext* inContext)
|
||||
{
|
||||
LPGLYPHSET charSet = This->charSet;
|
||||
|
||||
assert(inIndex >= 0);
|
||||
|
||||
if (charSet->cGlyphsSupported) {
|
||||
DWORD i = 0;
|
||||
LPWCRANGE range = charSet->ranges;
|
||||
GLCchar8* name = NULL;
|
||||
GLCchar* buffer = NULL;
|
||||
|
||||
for (; i < charSet->cRanges; i++, range++) {
|
||||
if (inIndex > range->cGlyphs) {
|
||||
inIndex -= range->cGlyphs;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get the character name */
|
||||
name = __glcNameFromCode(range->wcLow + inIndex);
|
||||
|
||||
if (!name) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
/* Performs the conversion to the current string type */
|
||||
buffer = __glcConvertFromUtf8ToBuffer(inContext, name,
|
||||
inContext->stringState.stringType);
|
||||
if (!buffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* The character has not been found */
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return GLC_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the number of characters in the character map */
|
||||
GLint __glcCharMapGetCount(__GLCcharMap* This)
|
||||
{
|
||||
return This->charSet->cGlyphsSupported;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the maximum mapped code of a character set */
|
||||
GLint __glcCharMapGetMaxMappedCode(__GLCcharMap* This)
|
||||
{
|
||||
DWORD i = 0;
|
||||
LPGLYPHSET charSet = This->charSet;
|
||||
LPWCRANGE range = charSet->ranges;
|
||||
GLCulong maxMappedCode = 0;
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int length = 0;
|
||||
|
||||
assert(charSet->cGlyphsSupported);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
|
||||
for (; i < charSet->cRanges; i++, range++) {
|
||||
WCHAR maxCode = range->wcLow + range->cGlyphs - 1;
|
||||
|
||||
maxMappedCode < maxCode ? maxCode : maxMappedCode;
|
||||
}
|
||||
|
||||
/* Check that a code greater than the one found in the GLYPHSET is not
|
||||
* stored in the array 'map'.
|
||||
*/
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
length = GLC_ARRAY_LENGTH(This->map);
|
||||
|
||||
/* Return the greater of the code of both the GLYPHSET and the array 'map'*/
|
||||
if (length)
|
||||
return element[length-1].mappedCode > maxMappedCode ?
|
||||
element[length-1].mappedCode : maxMappedCode;
|
||||
else
|
||||
return maxMappedCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the minimum mapped code of a character set */
|
||||
GLint __glcCharMapGetMinMappedCode(__GLCcharMap* This)
|
||||
{
|
||||
DWORD i = 0;
|
||||
LPGLYPHSET charSet = This->charSet;
|
||||
LPWCRANGE range = charSet->ranges;
|
||||
GLCulong minMappedCode = 0xffffffff;
|
||||
__GLCcharMapElement* element = NULL;
|
||||
int length = 0;
|
||||
|
||||
assert(charSet->cGlyphsSupported);
|
||||
assert(This->map);
|
||||
assert(GLC_ARRAY_DATA(This->map));
|
||||
|
||||
for (; i < charSet->cRanges; i++, range++)
|
||||
minMappedCode > range->wcLow ? range->wcLow : minMappedCode;
|
||||
|
||||
/* Check that a code lower than the one found in the GLYPHSET is not
|
||||
* stored in the array 'map'.
|
||||
*/
|
||||
element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
|
||||
length = GLC_ARRAY_LENGTH(This->map);
|
||||
|
||||
/* Return the lower of the code of both the GLYPHSET and the array 'map' */
|
||||
if (length > 0)
|
||||
return element[0].mappedCode < minMappedCode ?
|
||||
element[0].mappedCode : minMappedCode;
|
||||
else
|
||||
return minMappedCode;
|
||||
}
|
|
@ -0,0 +1,773 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCcontext which is used to manage the contexts.
|
||||
*/
|
||||
|
||||
/* QuesoGLC needs Windows 2000 or newer */
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#include "internal.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <io.h>
|
||||
|
||||
#define BUFSIZE MAX_PATH
|
||||
|
||||
#include "texture.h"
|
||||
#include FT_MODULE_H
|
||||
|
||||
__GLCcommonArea __glcCommonArea;
|
||||
__GLCthreadArea* __glcThreadArea = NULL;
|
||||
|
||||
static void __glcContextUpdateHashTable(__GLCcontext *This);
|
||||
static LPTSTR __glcAddCatalog(const GLCchar* inCatalog);
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
*/
|
||||
__GLCcontext* __glcContextCreate(GLint inContext)
|
||||
{
|
||||
__GLCcontext *This = NULL;
|
||||
|
||||
This = (__GLCcontext*)__glcMalloc(sizeof(__GLCcontext));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
memset(This, 0, sizeof(__GLCcontext));
|
||||
|
||||
if (FT_New_Library(&__glcCommonArea.memoryManager, &This->library)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FT_Add_Default_Modules(This->library);
|
||||
|
||||
#ifdef FT_CACHE_H
|
||||
if (FTC_Manager_New(This->library, 0, 0, 0, __glcFileOpen, NULL,
|
||||
&This->cache)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
This->node.prev = NULL;
|
||||
This->node.next = NULL;
|
||||
This->node.data = NULL;
|
||||
|
||||
This->catalogList = __glcArrayCreate(sizeof(GLCchar8*));
|
||||
if (!This->catalogList) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->masterHashTable = __glcArrayCreate(sizeof(GLCchar32));
|
||||
if (!This->masterHashTable) {
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
__glcContextUpdateHashTable(This);
|
||||
|
||||
This->currentFontList.head = NULL;
|
||||
This->currentFontList.tail = NULL;
|
||||
This->fontList.head = NULL;
|
||||
This->fontList.tail = NULL;
|
||||
|
||||
This->isCurrent = GL_FALSE;
|
||||
This->isInGlobalCommand = GL_FALSE;
|
||||
This->id = inContext;
|
||||
This->pendingDelete = GL_FALSE;
|
||||
This->stringState.callback = GLC_NONE;
|
||||
This->stringState.dataPointer = NULL;
|
||||
This->stringState.replacementCode = 0;
|
||||
This->stringState.stringType = GLC_UCS1;
|
||||
This->enableState.autoFont = GL_TRUE;
|
||||
This->enableState.glObjects = GL_TRUE;
|
||||
This->enableState.mipmap = GL_TRUE;
|
||||
This->enableState.hinting = GL_FALSE;
|
||||
This->enableState.extrude = GL_FALSE;
|
||||
This->enableState.kerning = GL_FALSE;
|
||||
This->renderState.resolution = 0.;
|
||||
This->renderState.renderStyle = GLC_BITMAP;
|
||||
This->bitmapMatrixStackDepth = 1;
|
||||
This->bitmapMatrix = This->bitmapMatrixStack;
|
||||
This->bitmapMatrix[0] = 1.;
|
||||
This->bitmapMatrix[1] = 0.;
|
||||
This->bitmapMatrix[2] = 0.;
|
||||
This->bitmapMatrix[3] = 1.;
|
||||
This->attribStackDepth = 0;
|
||||
This->measurementBuffer = __glcArrayCreate(12 * sizeof(GLfloat));
|
||||
if (!This->measurementBuffer) {
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
This->isInCallbackFunc = GL_FALSE;
|
||||
This->buffer = NULL;
|
||||
This->bufferSize = 0;
|
||||
This->lastFontID = 1;
|
||||
This->vertexArray = __glcArrayCreate(2 * sizeof(GLfloat));
|
||||
if (!This->vertexArray) {
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
This->controlPoints = __glcArrayCreate(5 * sizeof(GLfloat));
|
||||
if (!This->controlPoints) {
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
This->endContour = __glcArrayCreate(sizeof(int));
|
||||
if (!This->endContour) {
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->vertexIndices = __glcArrayCreate(sizeof(GLuint));
|
||||
if (!This->vertexIndices) {
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->endContour);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->geomBatches = __glcArrayCreate(sizeof(__GLCgeomBatch));
|
||||
if (!This->geomBatches) {
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
__glcArrayDestroy(This->endContour);
|
||||
__glcArrayDestroy(This->vertexIndices);
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This->texture.id = 0;
|
||||
This->texture.width = 0;
|
||||
This->texture.heigth = 0;
|
||||
This->texture.bufferObjectID = 0;
|
||||
|
||||
This->atlas.id = 0;
|
||||
This->atlas.width = 0;
|
||||
This->atlas.heigth = 0;
|
||||
This->atlasList.head = NULL;
|
||||
This->atlasList.tail = NULL;
|
||||
This->atlasWidth = 0;
|
||||
This->atlasHeight = 0;
|
||||
This->atlasCount = 0;
|
||||
|
||||
/* The environment variable GLC_PATH is an alternate way to allow QuesoGLC
|
||||
* to access to fonts catalogs/directories.
|
||||
*/
|
||||
/*Check if the GLC_PATH environment variables are exported */
|
||||
if (getenv("GLC_CATALOG_LIST") || getenv("GLC_PATH")) {
|
||||
char *path = NULL;
|
||||
char *begin = NULL;
|
||||
char *sepPos = NULL;
|
||||
char *separator = NULL;
|
||||
|
||||
/* Read the paths of fonts file.
|
||||
* First, try GLC_CATALOG_LIST...
|
||||
*/
|
||||
if (getenv("GLC_CATALOG_LIST"))
|
||||
path = strdup(getenv("GLC_CATALOG_LIST"));
|
||||
else if (getenv("GLC_PATH")) {
|
||||
/* Try GLC_PATH which uses the same format than PATH */
|
||||
path = strdup(getenv("GLC_PATH"));
|
||||
}
|
||||
|
||||
/* Get the list separator */
|
||||
separator = getenv("GLC_LIST_SEPARATOR");
|
||||
if (!separator) {
|
||||
#ifdef __WIN32__
|
||||
/* Windows can not use a colon-separated list since the colon sign is
|
||||
* used after the drive letter. The semicolon is used for the PATH
|
||||
* variable, so we use it for consistency.
|
||||
*/
|
||||
separator = (char *)";";
|
||||
#else
|
||||
/* POSIX platforms uses colon-separated lists for the paths variables
|
||||
* so we keep with it for consistency.
|
||||
*/
|
||||
separator = (char *)":";
|
||||
#endif
|
||||
}
|
||||
|
||||
if (path) {
|
||||
/* Get each path and add the corresponding masters to the current
|
||||
* context */
|
||||
LPTSTR dup = NULL;
|
||||
|
||||
begin = path;
|
||||
do {
|
||||
sepPos = (char *)__glcFindIndexList(begin, 1, separator);
|
||||
|
||||
if (*sepPos)
|
||||
*(sepPos++) = 0;
|
||||
|
||||
dup = __glcAddCatalog(begin);
|
||||
if (!dup) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
else {
|
||||
if (!__glcArrayAppend(This->catalogList, &dup)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(dup);
|
||||
}
|
||||
}
|
||||
|
||||
begin = sepPos;
|
||||
} while (*sepPos);
|
||||
free(path);
|
||||
__glcContextUpdateHashTable(This);
|
||||
}
|
||||
else {
|
||||
/* strdup has failed to allocate memory to duplicate GLC_PATH => ERROR */
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function is called from FT_List_Finalize() to destroy all
|
||||
* remaining fonts
|
||||
*/
|
||||
static void __glcFontDestructor(FT_Memory GLC_UNUSED_ARG(inMemory),
|
||||
void* inData, void* inUser)
|
||||
{
|
||||
__GLCfont *font = (__GLCfont*)inData;
|
||||
__GLCcontext* ctx = (__GLCcontext*)inUser;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
if (font)
|
||||
__glcFontDestroy(font, ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object : it first destroys all the GLC objects that have
|
||||
* been created during the life of the context. Then it releases the memory
|
||||
* occupied by the GLC state struct.
|
||||
* It does not destroy GL objects associated with the GLC context since we can
|
||||
* not be sure that the current GL context is the GL context that contains the
|
||||
* GL objects designated by the GLC context that we are destroying. This could
|
||||
* happen if the user calls glcDeleteContext() after the GL context has been
|
||||
* destroyed of after the user has changed the current GL context.
|
||||
*/
|
||||
void __glcContextDestroy(__GLCcontext *This)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
assert(This);
|
||||
|
||||
/* Destroy the list of catalogs */
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(This->catalogList); i++) {
|
||||
GLCchar8* string = ((GLCchar8**)GLC_ARRAY_DATA(This->catalogList))[i];
|
||||
|
||||
assert(string);
|
||||
free(string);
|
||||
}
|
||||
__glcArrayDestroy(This->catalogList);
|
||||
|
||||
/* Destroy GLC_CURRENT_FONT_LIST */
|
||||
FT_List_Finalize(&This->currentFontList, NULL,
|
||||
&__glcCommonArea.memoryManager, NULL);
|
||||
|
||||
/* Destroy GLC_FONT_LIST */
|
||||
FT_List_Finalize(&This->fontList, __glcFontDestructor,
|
||||
&__glcCommonArea.memoryManager, This);
|
||||
|
||||
if (This->masterHashTable)
|
||||
__glcArrayDestroy(This->masterHashTable);
|
||||
|
||||
FT_List_Finalize(&This->atlasList, NULL,
|
||||
&__glcCommonArea.memoryManager, NULL);
|
||||
|
||||
if (This->bufferSize)
|
||||
__glcFree(This->buffer);
|
||||
|
||||
if (This->measurementBuffer)
|
||||
__glcArrayDestroy(This->measurementBuffer);
|
||||
|
||||
if (This->vertexArray)
|
||||
__glcArrayDestroy(This->vertexArray);
|
||||
|
||||
if (This->controlPoints)
|
||||
__glcArrayDestroy(This->controlPoints);
|
||||
|
||||
if (This->endContour)
|
||||
__glcArrayDestroy(This->endContour);
|
||||
|
||||
if (This->vertexIndices)
|
||||
__glcArrayDestroy(This->vertexIndices);
|
||||
|
||||
if (This->geomBatches)
|
||||
__glcArrayDestroy(This->geomBatches);
|
||||
|
||||
#ifdef FT_CACHE_H
|
||||
FTC_Manager_Done(This->cache);
|
||||
#endif
|
||||
FT_Done_Library(This->library);
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the first font in GLC_CURRENT_FONT_LIST that maps 'inCode'.
|
||||
* If there is no such font, the function returns NULL.
|
||||
* 'inCode' must be given in UCS-4 format.
|
||||
*/
|
||||
static __GLCfont* __glcLookupFont(GLint inCode, FT_List fontList)
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
|
||||
for (node = fontList->head; node; node = node->next) {
|
||||
__GLCfont* font = (__GLCfont*)node->data;
|
||||
|
||||
/* Check if the character identified by inCode exists in the font */
|
||||
if (__glcCharMapHasChar(font->charMap, inCode))
|
||||
return font;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Calls the callback function (does various tests to determine if it is
|
||||
* possible) and returns GL_TRUE if it has succeeded or GL_FALSE otherwise.
|
||||
* 'inCode' must be given in UCS-4 format.
|
||||
*/
|
||||
static GLboolean __glcCallCallbackFunc(GLint inCode,
|
||||
__GLCcontext *inContext)
|
||||
{
|
||||
GLCfunc callbackFunc = NULL;
|
||||
GLboolean result = GL_FALSE;
|
||||
GLint aCode = 0;
|
||||
|
||||
/* Recursivity is not allowed */
|
||||
if (inContext->isInCallbackFunc)
|
||||
return GL_FALSE;
|
||||
|
||||
callbackFunc = inContext->stringState.callback;
|
||||
if (!callbackFunc)
|
||||
return GL_FALSE;
|
||||
|
||||
/* Convert the character code back to the current string type */
|
||||
aCode = __glcConvertUcs4ToGLint(inContext, inCode);
|
||||
/* Check if the character has been converted */
|
||||
if (aCode < 0)
|
||||
return GL_FALSE;
|
||||
|
||||
inContext->isInCallbackFunc = GL_TRUE;
|
||||
/* Call the callback function with the character converted to the current
|
||||
* string type.
|
||||
*/
|
||||
result = (*callbackFunc)(aCode);
|
||||
inContext->isInCallbackFunc = GL_FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Returns the ID of the first font in GLC_CURRENT_FONT_LIST that maps
|
||||
* 'inCode'. If there is no such font and GLC_AUTO_FONT is enabled, the
|
||||
* function attempts to append a new font from GLC_FONT_LIST (or from a master)
|
||||
* to GLC_CURRENT_FONT_LIST. If the attempt fails the function returns zero.
|
||||
* 'inCode' must be given in UCS-4 format.
|
||||
*/
|
||||
__GLCfont* __glcContextGetFont(__GLCcontext *This, GLint inCode)
|
||||
{
|
||||
__GLCfont* font = NULL;
|
||||
|
||||
/* Look for a font in the current font list */
|
||||
font = __glcLookupFont(inCode, &This->currentFontList);
|
||||
/* If a font has been found return */
|
||||
if (font)
|
||||
return font;
|
||||
|
||||
/* If a callback function is defined for GLC_OP_glcUnmappedCode then call it.
|
||||
* The callback function should return GL_TRUE if it succeeds in appending to
|
||||
* GLC_CURRENT_FONT_LIST the ID of a font that maps 'inCode'.
|
||||
*/
|
||||
if (__glcCallCallbackFunc(inCode, This)) {
|
||||
font = __glcLookupFont(inCode, &This->currentFontList);
|
||||
if (font)
|
||||
return font;
|
||||
}
|
||||
|
||||
/* If the value of the boolean variable GLC_AUTOFONT is GL_TRUE then search
|
||||
* GLC_FONT_LIST for the first font that maps 'inCode'. If the search
|
||||
* succeeds, then append the font's ID to GLC_CURRENT_FONT_LIST.
|
||||
*/
|
||||
if (This->enableState.autoFont) {
|
||||
__GLCmaster* master = NULL;
|
||||
|
||||
font = __glcLookupFont(inCode, &This->fontList);
|
||||
if (font) {
|
||||
__glcAppendFont(This, font);
|
||||
return font;
|
||||
}
|
||||
|
||||
master = __glcMasterMatchCode(This, inCode);
|
||||
if (!master)
|
||||
return NULL;
|
||||
font = __glcNewFontFromMaster(NULL, glcGenFontID(), master, This);
|
||||
__glcMasterDestroy(master);
|
||||
|
||||
if (font) {
|
||||
/* Add the font to the GLC_CURRENT_FONT_LIST */
|
||||
__glcAppendFont(This, font);
|
||||
return font;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Sometimes informations may need to be stored temporarily by a thread.
|
||||
* The so-called 'buffer' is created for that purpose. Notice that it is a
|
||||
* component of the GLC state struct hence its lifetime is the same as the
|
||||
* GLC state's lifetime.
|
||||
* __glcCtxQueryBuffer() should be called whenever the buffer is to be used
|
||||
* in order to check if it is big enough to store infos.
|
||||
* Note that the only memory management function used below is 'realloc' which
|
||||
* means that the buffer goes bigger and bigger until it is freed. No function
|
||||
* is provided to reduce its size so it should be freed and re-allocated
|
||||
* manually in case of emergency ;-)
|
||||
*/
|
||||
GLCchar* __glcContextQueryBuffer(__GLCcontext *This, int inSize)
|
||||
{
|
||||
GLCchar* buffer;
|
||||
|
||||
buffer = This->buffer;
|
||||
if (inSize > This->bufferSize) {
|
||||
buffer = (GLCchar*)__glcRealloc(This->buffer, inSize);
|
||||
if (!buffer) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
else {
|
||||
This->buffer = buffer;
|
||||
This->bufferSize = inSize;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumUpdateHashTable(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCarray* masterHashTable = (__GLCarray*)inData;
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(masterHashTable);
|
||||
TCHAR* ptr = NULL;
|
||||
int length = GLC_ARRAY_LENGTH(masterHashTable);
|
||||
int i = 0;
|
||||
GLCchar32 hashValue = 0;
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
hashValue = __glcHashValue(&inElfe->elfLogFont);
|
||||
|
||||
/* Check if the font is already registered in the hash table */
|
||||
for (i = 0; i < length; i++) {
|
||||
if (hashTable[i] == hashValue)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the font is already registered then parse the next one */
|
||||
if (i != length)
|
||||
return 1;
|
||||
|
||||
/* Register the font (i.e. append its hash value to the hash table) */
|
||||
if (!__glcArrayAppend(masterHashTable, &hashValue)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Update the hash table that allows to convert master IDs into FontConfig
|
||||
* patterns.
|
||||
*/
|
||||
static void __glcContextUpdateHashTable(__GLCcontext *This)
|
||||
{
|
||||
int i = 0;
|
||||
LOGFONT lfont;
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumUpdateHashTable,
|
||||
(LPARAM)This->masterHashTable, 0);
|
||||
|
||||
DeleteDC(dc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static LPTSTR __glcAddCatalog(const GLCchar* inCatalog)
|
||||
{
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
LPTSTR DirSpec = NULL;
|
||||
size_t length = 0;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
TCHAR* ptr;
|
||||
|
||||
DirSpec = (LPTSTR) __glcMalloc(BUFSIZE);
|
||||
if (!DirSpec) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check that input is not larger than allowed */
|
||||
StringCbLength(inCatalog, BUFSIZE, &length);
|
||||
if (length > (BUFSIZE - 2)) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
__glcFree(DirSpec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StringCbCopyN(DirSpec, BUFSIZE, inCatalog, length+1);
|
||||
StringCbCatN(DirSpec, BUFSIZE, TEXT("\\*"), 2*sizeof(TCHAR));
|
||||
|
||||
hFind = FindFirstFile(DirSpec, &FindFileData);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
__glcFree(DirSpec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AddFontResourceEx(FindFileData.cFileName, FR_PRIVATE, 0);
|
||||
|
||||
while (FindNextFile(hFind, &FindFileData))
|
||||
AddFontResourceEx(FindFileData.cFileName, FR_PRIVATE, 0);
|
||||
|
||||
FindClose(hFind);
|
||||
|
||||
ptr = (TCHAR*)DirSpec;
|
||||
ptr[length+1] = (TCHAR)'\0';
|
||||
|
||||
return DirSpec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Append a catalog to the context catalog list */
|
||||
void __glcContextAppendCatalog(__GLCcontext* This, const GLCchar* inCatalog)
|
||||
{
|
||||
LPTSTR DirSpec = __glcAddCatalog(inCatalog);
|
||||
|
||||
if (!DirSpec)
|
||||
return;
|
||||
|
||||
if (!__glcArrayAppend(This->catalogList, DirSpec)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
__glcFree(DirSpec);
|
||||
return;
|
||||
}
|
||||
__glcContextUpdateHashTable(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Prepend a catalog to the context catalog list */
|
||||
void __glcContextPrependCatalog(__GLCcontext* This, const GLCchar* inCatalog)
|
||||
{
|
||||
LPTSTR DirSpec = __glcAddCatalog(inCatalog);
|
||||
|
||||
if (!DirSpec)
|
||||
return;
|
||||
|
||||
if (!__glcArrayInsert(This->catalogList, 0, &dup)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
free(dup);
|
||||
return;
|
||||
}
|
||||
|
||||
__glcContextUpdateHashTable(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the path of the catalog identified by inIndex */
|
||||
GLCchar8* __glcContextGetCatalogPath(__GLCcontext* This, GLint inIndex)
|
||||
{
|
||||
if (inIndex >= GLC_ARRAY_LENGTH(This->catalogList)) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ((GLCchar8**)GLC_ARRAY_DATA(This->catalogList))[inIndex];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Remove a catalog from the context catalog list */
|
||||
void __glcContextRemoveCatalog(__GLCcontext* This, GLint inIndex)
|
||||
{
|
||||
FT_ListNode node = NULL;
|
||||
int i = 0;
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
LPTSTR DirSpec = NULL;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (inIndex >= GLC_ARRAY_LENGTH(This->catalogList)) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
DirSpec = ((LPTSTR*)GLC_ARRAY_DATA(This->catalogList))[inIndex];
|
||||
assert(DirSpec);
|
||||
__glcArrayRemove(This->catalogList, inIndex);
|
||||
StringCbCatN(DirSpec, BUFSIZE, TEXT("\\*"), 2*sizeof(TCHAR));
|
||||
|
||||
hFind = FindFirstFile(DirSpec, &FindFileData);
|
||||
if (hFind == INVALID_HANDLE_VALUE) {
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
__glcFree(DirSpec);
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveFontResourceEx(FindFileData.cFileName, FR_PRIVATE, 0);
|
||||
|
||||
while (FindNextFile(hFind, &FindFileData))
|
||||
RemoveFontResourceEx(FindFileData.cFileName, FR_PRIVATE, 0);
|
||||
|
||||
FindClose(hFind);
|
||||
__glcFree(DirSpec);
|
||||
|
||||
/* Re-create the hash table from scratch */
|
||||
GLC_ARRAY_LENGTH(This->masterHashTable) = 0;
|
||||
__glcContextUpdateHashTable(This);
|
||||
|
||||
/* Remove from GLC_FONT_LIST the fonts that were defined in the catalog that
|
||||
* has been removed.
|
||||
*/
|
||||
for (node = This->fontList.head; node; node = node->next) {
|
||||
__GLCfont* font = (__GLCfont*)(node->data);
|
||||
__GLCmaster* master = __glcMasterCreate(font->parentMasterID, This);
|
||||
GLCchar32 hashValue = 0;
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(This->masterHashTable);
|
||||
int length = GLC_ARRAY_LENGTH(This->masterHashTable);
|
||||
int i = 0;
|
||||
|
||||
if (!master) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
continue;
|
||||
}
|
||||
/* Check if the hash value of the master is in the hash table */
|
||||
hashValue = GLC_MASTER_HASH_VALUE(master);
|
||||
for (i = 0; i < length; i++) {
|
||||
if (hashValue == hashTable[i])
|
||||
break;
|
||||
}
|
||||
|
||||
/* The font is not contained in the hash table => remove it */
|
||||
if (i == length)
|
||||
glcDeleteFont(font->id);
|
||||
|
||||
__glcMasterDestroy(master);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,775 @@
|
|||
/* QuesoGLC
|
||||
* A free implementation of the OpenGL Character Renderer (GLC)
|
||||
* Copyright (c) 2002, 2004-2007, Bertrand Coconnier
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
/** \file
|
||||
* defines the object __GLCmaster which manage the masters
|
||||
*/
|
||||
|
||||
/* QuesoGLC needs Windows 2000 or newer */
|
||||
#define _WIN32_WINNT 0x0500
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
|
||||
typedef struct __GLCdataRec __GLCdata;
|
||||
|
||||
struct __GLCdataRec {
|
||||
__GLCarray* hashTable;
|
||||
__GLCmaster* master;
|
||||
__GLCcharMap* charMap;
|
||||
void* ptr;
|
||||
GLint index;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Hash function of strings adapted from "Compilers : Principles, Techniques and
|
||||
* Tools - Alfred V. Aho, Ravi Sethi and Jeffrey D. Ullman"
|
||||
*/
|
||||
GLCchar32 __glcHashValue(LPLOGFONT inLogFont)
|
||||
{
|
||||
TCHAR* ptr = inLogFont->lfFaceName;
|
||||
GLCchar32 tmp = 0;
|
||||
GLCchar32 hashValue = 0;
|
||||
|
||||
while(*ptr) {
|
||||
hashValue = (hashValue << 4) + (*ptr);
|
||||
tmp = hashValue & 0xf0000000;
|
||||
if (tmp) {
|
||||
hashValue = hashValue ^ (tmp >> 24);
|
||||
hashValue = hashValue ^ tmp;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterCreate(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCarray* masterHashTable = ((__GLCdata*)inData)->hashTable;
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(masterHashTable);
|
||||
int length = GLC_ARRAY_LENGTH(masterHashTable);
|
||||
int i = 0;
|
||||
GLCchar32 hashValue = 0;
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
hashValue = __glcHashValue(&inElfe->elfLogFont);
|
||||
|
||||
/* Check if the font is already registered in the hash table */
|
||||
for (i = 0; i < length; i++) {
|
||||
if (hashTable[i] == hashValue) {
|
||||
__GLCmaster* master = ((__GLCdata*)inData)->master;
|
||||
memcpy(master->pattern, &inElfe->elfLogFont, sizeof(ENUMLOGFONTEX));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Constructor of the object : it allocates memory and initializes the member
|
||||
* of the new object.
|
||||
*/
|
||||
__GLCmaster* __glcMasterCreate(GLint inMaster, __GLCcontext* inContext)
|
||||
{
|
||||
__GLCmaster* This = NULL;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
LOGFONT lfont;
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This = (__GLCmaster*)__glcMalloc(sizeof(__GLCmaster));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data.hashTable = inContext->masterHashTable;
|
||||
data.master = This;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterCreate,
|
||||
(LPARAM)&data, 0);
|
||||
|
||||
DeleteDC(dc);
|
||||
return This;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Destructor of the object */
|
||||
void __glcMasterDestroy(__GLCmaster* This)
|
||||
{
|
||||
__glcFree(This->pattern);
|
||||
__glcFree(This);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Merge the character map 'inCharMap' in the character map 'This' */
|
||||
static GLboolean __glcCharMapUnion(__GLCcharMap* This, LPGLYPHSET inCharSet)
|
||||
{
|
||||
LPGLYPHSET result = NULL;
|
||||
LPGLYPHSET charSet = This->charSet;
|
||||
DWORD i = 0;
|
||||
DWORD resultSize = 0;
|
||||
LPWCRANGE range = NULL;
|
||||
|
||||
assert(charSet);
|
||||
assert(inCharSet);
|
||||
|
||||
if (!inCharSet->cGlyphsSupported)
|
||||
return GL_TRUE;
|
||||
|
||||
if (!charSet->cGlyphsSupported)
|
||||
resultSize = inCharSet->cbThis;
|
||||
else
|
||||
resultSize = charSet->cbThis + inCharSet->cRanges * sizeof(WCRANGE);
|
||||
|
||||
result = (LPGLYPHSET)__glcMalloc(resultSize);
|
||||
if (!result) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
if (!charSet->cGlyphsSupported) {
|
||||
memcpy(result, inCharSet, inCharSet->cbThis);
|
||||
__glcFree(charSet);
|
||||
This->charSet = result;
|
||||
return GL_TRUE;
|
||||
}
|
||||
else {
|
||||
memcpy(result, charSet, charSet->cbThis);
|
||||
result->cbThis = resultSize;
|
||||
}
|
||||
|
||||
for (; i < inCharSet->cRanges; i++) {
|
||||
DWORD j = 0;
|
||||
LPWCRANGE range2 = result->ranges;
|
||||
WCRANGE temp;
|
||||
|
||||
range = inCharSet->ranges + i;
|
||||
|
||||
for (; j < result->cRanges; j++, range2++) {
|
||||
if (range->wcLow > (range2->wcLow + range2->cGlyphs -1))
|
||||
continue;
|
||||
if ((range->wcLow + range->cGlyphs - 1) < range2->wcLow) {
|
||||
memcpy(range2+1, range2, (result->cRanges - j) * sizeof(WCRANGE));
|
||||
range2->wcLow = range->wcLow;
|
||||
range2->cGlyphs = range2->cGlyphs;
|
||||
result->cRanges++;
|
||||
break;
|
||||
}
|
||||
if ((range->wcLow >= range2->wcLow) && ((range->wcLow + range->cGlyphs) <= (range2->wcLow + range2->cGlyphs)))
|
||||
break;
|
||||
if ((range->wcLow < range2->wcLow) && ((range->wcLow + range->cGlyphs) <= (range2->wcLow + range2->cGlyphs))) {
|
||||
range2->cGlyphs = range2->wcLow + range2->cGlyphs - range->wcLow;
|
||||
range2->wcLow = range->wcLow;
|
||||
break;
|
||||
}
|
||||
temp.wcLow = (range->wcLow < range2->wcLow) ? range->wcLow : range2->wcLow;
|
||||
temp.cGlyphs = range2->wcLow + range2->cGlyphs - temp.wcLow;
|
||||
range = &temp;
|
||||
if (result->cRanges-j-1) {
|
||||
memcpy(range2-1, range2, (result->cRanges-j-1)*sizeof(WCRANGE));
|
||||
result->cRanges--;
|
||||
j--;
|
||||
range2--;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == result->cRanges) {
|
||||
range2->wcLow = range->wcLow;
|
||||
range2->cGlyphs = range->cGlyphs;
|
||||
result->cRanges++;
|
||||
}
|
||||
}
|
||||
|
||||
range = result->ranges;
|
||||
result->cGlyphsSupported = 0;
|
||||
|
||||
for (i = 0; i < result->cRanges; i++, range++)
|
||||
result->cGlyphsSupported += range->cGlyphs;
|
||||
|
||||
/* Destroy the previous GLYPHSET and replace it by the new one */
|
||||
__glcFree(This->charSet);
|
||||
This->charSet = result;
|
||||
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterGetCharMap(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCdata* data = (__GLCdata*)inData;
|
||||
GLCchar32 hashValue = GLC_MASTER_HASH_VALUE(data->master);
|
||||
HFONT font;
|
||||
DWORD size;
|
||||
HDC dc;
|
||||
LPGLYPHSET charSet = NULL;
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
if (hashValue != __glcHashValue(&inElfe->elfLogFont))
|
||||
return 1;
|
||||
|
||||
dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
font = CreateFontIndirect(&inElfe->elfLogFont);
|
||||
if (FAILED(font)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FAILED(SelectObject(dc, font))) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = GetFontUnicodeRanges(dc, NULL);
|
||||
if (!size) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
charSet = (LPGLYPHSET)__glcMalloc(size);
|
||||
if (!charSet) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!GetFontUnicodeRanges(dc, charSet)) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcFree(charSet);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcCharMapUnion(data->charMap, charSet);
|
||||
__glcFree(charSet);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Extract the charmap of the master */
|
||||
__GLCcharMap* __glcMasterGetCharMap(__GLCmaster* This, __GLCcontext* inContext)
|
||||
{
|
||||
LOGFONT lfont;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
__GLCcharMap* charMap = NULL;
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
charMap = __glcCharMapCreate(This);
|
||||
if (!charMap) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data.master = This;
|
||||
data.charMap = charMap;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterGetCharMap,
|
||||
(LPARAM)&data, 0);
|
||||
|
||||
DeleteDC(dc);
|
||||
return charMap;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterGetFaceName(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCdata* data = (__GLCdata*)inData;
|
||||
GLCchar32 hashValue = GLC_MASTER_HASH_VALUE(data->master);
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
if (hashValue != __glcHashValue(&inElfe->elfLogFont))
|
||||
return 1;
|
||||
|
||||
if (--data->index)
|
||||
return 1;
|
||||
|
||||
data->ptr = __glcMalloc(LF_FACESIZE * sizeof(TCHAR));
|
||||
if (!data->ptr) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(data->ptr, inElfe->elfStyle, LF_FACESIZE * sizeof(TCHAR));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the style name of the face identified by inIndex */
|
||||
GLCchar8* __glcMasterGetFaceName(__GLCmaster* This, __GLCcontext* inContext,
|
||||
GLint inIndex)
|
||||
{
|
||||
LOGFONT lfont;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data.master = This;
|
||||
data.index = inIndex;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
if (EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterGetFaceName,
|
||||
(LPARAM)&data, 0)) {
|
||||
/* Could not find face identified by inIndex */
|
||||
assert(data.index);
|
||||
__glcRaiseError(GLC_PARAMETER_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(!data.index);
|
||||
|
||||
DeleteDC(dc);
|
||||
return (GLCchar8*)data.ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterIsFixedPitch(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCdata* data = (__GLCdata*)inData;
|
||||
GLCchar32 hashValue = GLC_MASTER_HASH_VALUE(data->master);
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
if (hashValue == __glcHashValue(&inElfe->elfLogFont))
|
||||
data->index += (inNtme->ntmTm.tmPitchAndFamily & 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Is this a fixed font ? */
|
||||
GLboolean __glcMasterIsFixedPitch(__GLCmaster* This)
|
||||
{
|
||||
LOGFONT lfont;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
data.master = This;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterGetFaceName,
|
||||
(LPARAM)&data, 0);
|
||||
DeleteDC(dc);
|
||||
|
||||
return data.index ? GL_FALSE : GL_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterFaceCount(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCdata* data = (__GLCdata*)inData;
|
||||
GLCchar32 hashValue = GLC_MASTER_HASH_VALUE(data->master);
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
if (hashValue == __glcHashValue(&inElfe->elfLogFont))
|
||||
data->index++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the face count of the master */
|
||||
GLint __glcMasterFaceCount(__GLCmaster* This, __GLCcontext* inContext)
|
||||
{
|
||||
LOGFONT lfont;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
data.master = This;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterGetFaceName,
|
||||
(LPARAM)&data, 0);
|
||||
DeleteDC(dc);
|
||||
|
||||
return data.index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This subroutine is called whenever the user wants to access to informations
|
||||
* that have not been loaded from the font files yet. In order to reduce disk
|
||||
* accesses, informations such as the master format, full name or version are
|
||||
* read "just in time" i.e. only when the user requests them.
|
||||
*/
|
||||
GLCchar8* __glcMasterGetInfo(__GLCmaster* This, __GLCcontext* inContext,
|
||||
GLCenum inAttrib)
|
||||
{
|
||||
TCHAR* string = NULL;
|
||||
GLCchar *buffer = NULL;
|
||||
static TCHAR unknown[] = TEXT("Unknown");
|
||||
#ifdef UNICODE
|
||||
int size = 0;
|
||||
size_t length = 0;
|
||||
LPSTR stringUTF8 = NULL;
|
||||
#endif
|
||||
|
||||
/* Get the Unicode string which corresponds to the requested attribute */
|
||||
switch(inAttrib) {
|
||||
case GLC_FAMILY:
|
||||
string = This->pattern->elfLogFont.lfFaceName;
|
||||
break;
|
||||
case GLC_FULL_NAME_SGI:
|
||||
string = This->pattern->elfFullName;
|
||||
break;
|
||||
case GLC_VERSION:
|
||||
case GLC_VENDOR:
|
||||
case GLC_MASTER_FORMAT:
|
||||
string = unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
StringCchLength(string, STRSAFE_MAX_CCH, &length);
|
||||
size = WideCharToMultiByte(CP_UTF8, 0, string, length, NULL, 0, NULL, NULL);
|
||||
stringUTF8 = (LPSTR)__glcMalloc(size);
|
||||
if (!stringUTF8) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
WideCharToMultiByte(CP_UTF8, 0, string, length, stringUTF8, size, NULL, NULL);
|
||||
string = stringUTF8;
|
||||
#endif
|
||||
|
||||
if (string) {
|
||||
/* Convert the string and store it in the context buffer */
|
||||
buffer = __glcConvertFromUtf8ToBuffer(inContext, string,
|
||||
inContext->stringState.stringType);
|
||||
|
||||
if (!buffer)
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
}
|
||||
else
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterFromFamily(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCdata* data = (__GLCdata*)inData;
|
||||
__GLCmaster* master = ((__GLCdata*)inData)->master;
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
if (lstrcmp(inElfe->elfLogFont.lfFaceName, data->ptr))
|
||||
return 1;
|
||||
|
||||
memcpy(master->pattern, &inElfe->elfLogFont, sizeof(ENUMLOGFONTEX));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a master on the basis of the family name */
|
||||
__GLCmaster* __glcMasterFromFamily(__GLCcontext* inContext, GLCchar8* inFamily)
|
||||
{
|
||||
__GLCmaster* This = NULL;
|
||||
TCHAR* family = NULL;
|
||||
LOGFONT lfont;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
#ifdef UNICODE
|
||||
int size = 0;
|
||||
#endif
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This = (__GLCmaster*)__glcMalloc(sizeof(__GLCmaster));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
int size = MultiByteToWideChar(CP_UTF8, 0, inFamily, -1, NULL, 0);
|
||||
|
||||
family = (LPWSTR)__glcMalloc(size * sizeof(WCHAR));
|
||||
if (!family) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
__glcFree(This);
|
||||
return NULL;
|
||||
}
|
||||
MultiByteToWideChar(CP_UTF8, 0, inFamily, -1, family, size);
|
||||
#else
|
||||
family = (TCHAR*)inFamily;
|
||||
#endif
|
||||
|
||||
data.master = This;
|
||||
data.ptr = family;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
if (EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterFromFamily,
|
||||
(LPARAM)&data, 0)) {
|
||||
__glcFree(This);
|
||||
This = NULL;
|
||||
data.master = NULL;
|
||||
}
|
||||
DeleteDC(dc);
|
||||
|
||||
return data.master;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int CALLBACK __glcEnumMasterMatchCode(ENUMLOGFONTEX* inElfe,
|
||||
NEWTEXTMETRICEX* inNtme,
|
||||
DWORD inFontType, LPARAM inData)
|
||||
{
|
||||
__GLCdata* data = (__GLCdata*)inData;
|
||||
GLint code = data->index;
|
||||
__GLCmaster* master = data->master;
|
||||
HFONT font;
|
||||
DWORD size;
|
||||
HDC dc;
|
||||
LPGLYPHSET charSet = NULL;
|
||||
|
||||
if (!(inFontType & TRUETYPE_FONTTYPE))
|
||||
return 1;
|
||||
|
||||
dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
font = CreateFontIndirect(&inElfe->elfLogFont);
|
||||
if (FAILED(font)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FAILED(SelectObject(dc, font))) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = GetFontUnicodeRanges(dc, NULL);
|
||||
if (!size) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
charSet = (LPGLYPHSET)__glcMalloc(size);
|
||||
if (!charSet) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!GetFontUnicodeRanges(dc, charSet)) {
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
__glcFree(charSet);
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
DeleteDC(dc);
|
||||
DeleteObject(font);
|
||||
|
||||
|
||||
/* Check if the character identified by inCode exists in the font */
|
||||
if (charSet->cGlyphsSupported) {
|
||||
DWORD i = 0;
|
||||
LPWCRANGE range = charSet->ranges;
|
||||
|
||||
for (; i < charSet->cRanges; i++, range++) {
|
||||
if (code > (range->wcLow + range->cGlyphs - 1))
|
||||
continue;
|
||||
if (code >= range->wcLow) {
|
||||
memcpy(master->pattern, &inElfe->elfLogFont, sizeof(ENUMLOGFONTEX));
|
||||
__glcFree(charSet);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
__glcFree(charSet);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a master which contains at least a font which match the character
|
||||
* identified by inCode.
|
||||
*/
|
||||
__GLCmaster* __glcMasterMatchCode(__GLCcontext* inContext, GLint inCode)
|
||||
{
|
||||
__GLCmaster* This = NULL;
|
||||
LOGFONT lfont;
|
||||
__GLCdata data = {NULL, NULL, NULL, NULL, 0};
|
||||
HDC dc = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
|
||||
|
||||
if (FAILED(dc)) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
This = (__GLCmaster*)__glcMalloc(sizeof(__GLCmaster));
|
||||
if (!This) {
|
||||
__glcRaiseError(GLC_RESOURCE_ERROR);
|
||||
DeleteDC(dc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data.master = This;
|
||||
data.index = inCode;
|
||||
|
||||
/* Enumerates all fonts in all character sets */
|
||||
lfont.lfCharSet = DEFAULT_CHARSET;
|
||||
lfont.lfFaceName[0] = '\0';
|
||||
|
||||
if (EnumFontFamiliesEx(dc, &lfont, (FONTENUMPROC)__glcEnumMasterMatchCode,
|
||||
(LPARAM)&data, 0)) {
|
||||
__glcFree(This);
|
||||
This = NULL;
|
||||
data.master = NULL;
|
||||
}
|
||||
DeleteDC(dc);
|
||||
|
||||
return data.master;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GLint __glcMasterGetID(__GLCmaster* This, __GLCcontext* inContext)
|
||||
{
|
||||
GLCchar32 hashValue = GLC_MASTER_HASH_VALUE(This);
|
||||
GLint i = 0;
|
||||
GLCchar32* hashTable = (GLCchar32*)GLC_ARRAY_DATA(inContext->masterHashTable);
|
||||
|
||||
for (i = 0; i < GLC_ARRAY_LENGTH(inContext->masterHashTable); i++) {
|
||||
if (hashValue == hashTable[i])
|
||||
break;
|
||||
}
|
||||
assert(i < GLC_ARRAY_LENGTH(inContext->masterHashTable));
|
||||
|
||||
return i;
|
||||
}
|
|
@ -7,6 +7,7 @@ endif
|
|||
SUBDIRS = \
|
||||
win32 \
|
||||
3rdparty/miniupnpc \
|
||||
3rdparty/quesoglc \
|
||||
lib/framework \
|
||||
lib/exceptionhandler \
|
||||
lib/script \
|
||||
|
|
17
configure.ac
17
configure.ac
|
@ -429,8 +429,20 @@ AC_CHECK_LIB(GLU, main,
|
|||
[${OPENGL_LIBS} -lm]), [${OPENGL_LIBS} -lm])
|
||||
AC_SUBST([OPENGL_LIBS], [${OPENGL_LIBS}])
|
||||
|
||||
# Look for OpenGLC
|
||||
PKG_CHECK_MODULES([OPENGLC], [quesoglc >= 0.7.2])
|
||||
# OpenGLC stuff
|
||||
ACX_PTHREAD
|
||||
CC="$PTHREAD_CC"
|
||||
AX_TLS
|
||||
|
||||
PKG_CHECK_MODULES([FRIBIDI], [fribidi])
|
||||
PKG_CHECK_MODULES([FREETYPE], [freetype2])
|
||||
AC_CHECK_LIB(freetype, FTC_Manager_New,
|
||||
AC_DEFINE([HAVE_FT_CACHE], [1],
|
||||
[Define if FreeType supports the caching routines]))
|
||||
PKG_CHECK_MODULES([FONTCONFIG], [fontconfig])
|
||||
|
||||
OPENGLC_LIBS="${FRIBIDI_LIBS} ${FREETYPE_LIBS} ${FONTCONFIG_LIBS} ${PTHREAD_LIBS} ${OPENGL_LIBS} ${GLEW_LIBS}"
|
||||
AC_SUBST([OPENGLC_LIBS], [${OPENGLC_LIBS}])
|
||||
|
||||
# When (cross-)compiling for Windows (MinGW) we need to link in BFD for the Dr.
|
||||
# MinGW derived exception handler.
|
||||
|
@ -467,6 +479,7 @@ AC_CONFIG_FILES([Makefile
|
|||
win32/Makefile
|
||||
tests/Makefile
|
||||
3rdparty/miniupnpc/Makefile
|
||||
3rdparty/quesoglc/Makefile
|
||||
lib/framework/Makefile
|
||||
lib/exceptionhandler/Makefile
|
||||
lib/gamelib/Makefile
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
dnl
|
||||
dnl This macro figures out how to build C programs using POSIX
|
||||
dnl threads. It sets the PTHREAD_LIBS output variable to the threads
|
||||
dnl library and linker flags, and the PTHREAD_CFLAGS output variable
|
||||
dnl to any special C compiler flags that are needed. (The user can also
|
||||
dnl force certain compiler flags/libs to be tested by setting these
|
||||
dnl environment variables.)
|
||||
dnl
|
||||
dnl Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
dnl multi-threaded programs (defaults to the value of CC otherwise).
|
||||
dnl (This is necessary on AIX to use the special cc_r compiler alias.)
|
||||
dnl
|
||||
dnl NOTE: You are assumed to not only compile your program with these
|
||||
dnl flags, but also link it with them as well. e.g. you should link
|
||||
dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
dnl
|
||||
dnl If you are only building threads programs, you may wish to
|
||||
dnl use these variables in your default LIBS, CFLAGS, and CC:
|
||||
dnl
|
||||
dnl LIBS="$PTHREAD_LIBS $LIBS"
|
||||
dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
dnl CC="$PTHREAD_CC"
|
||||
dnl
|
||||
dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
|
||||
dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE
|
||||
dnl to that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
dnl
|
||||
dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
|
||||
dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands
|
||||
dnl to run it if it is not found. If ACTION-IF-FOUND is not specified,
|
||||
dnl the default action will define HAVE_PTHREAD.
|
||||
dnl
|
||||
dnl Please let the authors know if this macro fails on any platform,
|
||||
dnl or if you have any other suggestions or comments. This macro was
|
||||
dnl based on work by SGJ on autoconf scripts for FFTW (www.fftw.org)
|
||||
dnl (with help from M. Frigo), as well as ac_pthread and hb_pthread
|
||||
dnl macros posted by AFC to the autoconf macro repository. We are also
|
||||
dnl grateful for the helpful feedback of numerous users.
|
||||
dnl
|
||||
dnl @version $Id$
|
||||
dnl @author Steven G. Johnson <stevenj@alum.mit.edu> and Alejandro Forero Cuervo <bachue@bachue.com>
|
||||
|
||||
AC_DEFUN([ACX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
acx_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
|
||||
AC_MSG_RESULT($acx_pthread_ok)
|
||||
if test x"$acx_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthread or
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$acx_pthread_ok" = xno; then
|
||||
for flag in $acx_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
|
||||
if test x"$acx_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[pthread_t th; pthread_join(th, 0);
|
||||
pthread_attr_init(0); pthread_cleanup_push(0, 0);
|
||||
pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
|
||||
[acx_pthread_ok=yes])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT($acx_pthread_ok)
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$acx_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: threads are created detached by default
|
||||
# and the JOINABLE attribute has a nonstandard name (UNDETACHED).
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[int attr=PTHREAD_CREATE_JOINABLE;],
|
||||
ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
|
||||
if test x"$ok" = xunknown; then
|
||||
AC_TRY_LINK([#include <pthread.h>],
|
||||
[int attr=PTHREAD_CREATE_UNDETACHED;],
|
||||
ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
|
||||
fi
|
||||
if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE(PTHREAD_CREATE_JOINABLE, "${ok}",
|
||||
[Define to the necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
AC_MSG_RESULT(${ok})
|
||||
if test x"$ok" = xunknown; then
|
||||
AC_MSG_WARN([we do not know how to create joinable pthreads])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*-aix* | *-freebsd*) flag="-D_THREAD_SAFE";;
|
||||
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
|
||||
esac
|
||||
AC_MSG_RESULT(${flag})
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# More AIX lossage: must compile with cc_r
|
||||
AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
|
||||
else
|
||||
PTHREAD_CC="$CC"
|
||||
fi
|
||||
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$acx_pthread_ok" = xyes; then
|
||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
||||
:
|
||||
else
|
||||
acx_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
])dnl ACX_PTHREAD
|
|
@ -0,0 +1,76 @@
|
|||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_tls.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_TLS([action-if-found], [action-if-not-found])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Provides a test for the compiler support of thread local storage (TLS)
|
||||
# extensions. Defines TLS if it is found. Currently knows about GCC/ICC
|
||||
# and MSVC. I think SunPro uses the same as GCC, and Borland apparently
|
||||
# supports either.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Alan Woodland <ajw05@aber.ac.uk>
|
||||
# Copyright (c) 2010 Diego Elio Petteno` <flameeyes@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 10
|
||||
|
||||
AC_DEFUN([AX_TLS], [
|
||||
AC_MSG_CHECKING(for thread local storage (TLS) class)
|
||||
AC_CACHE_VAL(ac_cv_tls, [
|
||||
ax_tls_keywords="__thread __declspec(thread) none"
|
||||
for ax_tls_keyword in $ax_tls_keywords; do
|
||||
AS_CASE([$ax_tls_keyword],
|
||||
[none], [ac_cv_tls=none ; break],
|
||||
[AC_TRY_COMPILE(
|
||||
[#include <stdlib.h>
|
||||
static void
|
||||
foo(void) {
|
||||
static ] $ax_tls_keyword [ int bar;
|
||||
exit(1);
|
||||
}],
|
||||
[],
|
||||
[ac_cv_tls=$ax_tls_keyword ; break],
|
||||
ac_cv_tls=none
|
||||
)])
|
||||
done
|
||||
])
|
||||
AC_MSG_RESULT($ac_cv_tls)
|
||||
|
||||
AS_IF([test "$ac_cv_tls" != "none"],
|
||||
AC_DEFINE_UNQUOTED([TLS], $ac_cv_tls, [If the compiler supports a TLS storage class define it to that here])
|
||||
m4_ifnblank([$1], [$1]),
|
||||
m4_ifnblank([$2], [$2])
|
||||
)
|
||||
])
|
|
@ -273,7 +273,8 @@ warzone2100_LIBS = \
|
|||
$(top_builddir)/lib/gamelib/libgamelib.a \
|
||||
$(top_builddir)/lib/framework/libframework.a \
|
||||
$(top_builddir)/lib/exceptionhandler/libexceptionhandler.a \
|
||||
$(top_builddir)/3rdparty/miniupnpc/libminiupnpc.a
|
||||
$(top_builddir)/3rdparty/miniupnpc/libminiupnpc.a \
|
||||
$(top_builddir)/3rdparty/quesoglc/libquesoglc.a
|
||||
|
||||
if BACKEND_QT
|
||||
warzone2100_LIBS += $(top_builddir)/lib/qtgame/libqtgame.a
|
||||
|
|
Loading…
Reference in New Issue