warzone2100/3rdparty/quesoglc/context.c

1541 lines
45 KiB
C

/* 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 "Context commands" described in chapter 3.5 of the GLC
* specs.
*/
/** \defgroup context Context State Commands
* Commands to get or modify informations of the context state of the current
* thread.
*
* GLC refers to the current context state whenever it executes a command.
* Most of its state is directly available to the user : in order to control
* the result of the GLC commands, the user may want to get or modify the
* state of the current context. This is precisely the purpose of the context
* state commands.
*
* \note Some GLC commands create, use or delete 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 \b 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 as the context where the display lists and the textures are
* actually stored. If the current GL context has changed meanwhile, the result
* of commands that refer to the corresponding display lists or textures is
* undefined.
*/
#include <string.h>
#include "internal.h"
/** \ingroup context
* This command assigns the value \e inFunc to the callback function variable
* identified by \e inOpCode which must be chosen in the following table.
* <center>
* <table>
* <caption>Callback function variables</caption>
* <tr>
* <td>Name</td>
* <td>Enumerant</td>
* <td>Initial value</td>
* <td>Type signature</td>
* </tr>
* <tr>
* <td><b>GLC_OP_glcUnmappedCode</b></td>
* <td>0x0020</td>
* <td><b>GLC_NONE</b></td>
* <td><b>GLboolean (*)(GLint)</b></td>
* </tr>
* </table>
* </center>
*
* The callback function can access to client data in a thread-safe manner
* with glcGetPointer().
* \param inOpcode Type of the callback function
* \param inFunc Callback function
* \sa glcGetCallbackFunc()
* \sa glcGetPointer()
* \sa glcDataPointer()
* \sa glcRenderChar()
*/
void APIENTRY glcCallbackFunc(GLCenum inOpcode, GLCfunc inFunc)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check parameters */
if (inOpcode != GLC_OP_glcUnmappedCode) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return;
}
ctx->stringState.callback = inFunc;
}
/** \ingroup context
* This command assigns the value \e inPointer to the variable
* \b GLC_DATA_POINTER. It is used for an access to client data from the
* callback function assigned to the variable \b GLC_OP_glcUnmappedCode
*
* \e glcDataPointer provides a way to store, in the GLC context, a pointer to
* any data. A GLC callback function can subsequently use the command
* glcGetPointer() to obtain access to those data in a thread-safe manner.
* \param inPointer The pointer to assign to \b GLC_DATA_POINTER
* \sa glcGetPointer()
* \sa glcCallbackFunc()
*/
void APIENTRY glcDataPointer(GLvoid *inPointer)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return;
}
ctx->stringState.dataPointer = inPointer;
}
/** \ingroup context
* This command causes GLC to issue a sequence of GL commands to delete all
* of the GL objects it owns.
*
* GLC uses the command \c glDeleteLists to delete all of the GL objects named
* in \b GLC_LIST_OBJECT_LIST and uses the command \c glDeleteTextures to
* delete all of the GL objects named in \b GLC_TEXTURE_OBJECT_LIST. When an
* execution of glcDeleteGLObjects finishes, both of these lists are empty.
* \note \c glcDeleteGLObjects deletes only the objects that the current
* GLC context owns, not all objects in all contexts.
* \note Generally speaking, it is always a good idea to call
* \c glcDeleteGLObjects before calling glcDeleteContext(). It is also a good
* idea to call \c glcDeleteGLObjects before changing the GL context that is
* associated with the current GLC context.
* \sa glcGetListi()
*/
void APIENTRY glcDeleteGLObjects(void)
{
__GLCcontext *ctx = NULL;
FT_ListNode node = NULL;
GLC_INIT_THREAD();
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return;
}
/* The GL objects are managed by the __GLCfaceDescriptor object. Hence we
* parse the __GLCfaceDescriptor stored in each __GLCfont.
*/
for(node = ctx->fontList.head; node; node = node->next)
__glcFaceDescDestroyGLObjects(((__GLCfont*)(node->data))->faceDesc, ctx);
/* Delete the texture used for immediate texture mode */
if (ctx->texture.id) {
glDeleteTextures(1, &ctx->texture.id);
ctx->texture.id = 0;
ctx->texture.width = 0;
ctx->texture.height = 0;
}
/* Delete the pixel buffer object used for immediate mode */
if (GLEW_ARB_pixel_buffer_object && ctx->texture.bufferObjectID) {
glDeleteBuffersARB(1, &ctx->texture.bufferObjectID);
ctx->texture.bufferObjectID = 0;
}
/* Delete the vertex buffer object used for the texture atlas */
if (GLEW_ARB_vertex_buffer_object && ctx->atlas.bufferObjectID) {
glDeleteBuffersARB(1, &ctx->atlas.bufferObjectID);
ctx->atlas.bufferObjectID = 0;
}
}
/* This internal function is used by both glcEnable/glcDisable which basically
* do the same job : put a value into a member of the __GLCcontext struct. The
* only difference is the value that they put (true or false).
*/
static void __glcChangeState(const GLCenum inAttrib, const GLboolean value)
{
__GLCcontext *ctx = NULL;
/* Check the parameters. */
assert((value == GL_TRUE) || (value == GL_FALSE));
switch(inAttrib) {
case GLC_AUTO_FONT:
case GLC_GL_OBJECTS:
case GLC_MIPMAP:
case GLC_HINTING_QSO: /* QuesoGLC Extension */
case GLC_EXTRUDE_QSO: /* QuesoGLC Extension */
case GLC_KERNING_QSO: /* QuesoGLC Extension */
break;
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return;
}
/* Assigns the value to the member identified by inAttrib */
switch(inAttrib) {
case GLC_AUTO_FONT:
ctx->enableState.autoFont = value;
break;
case GLC_GL_OBJECTS:
ctx->enableState.glObjects = value;
break;
case GLC_MIPMAP:
ctx->enableState.mipmap = value;
/* Update the mipmap setting of the texture atlas */
if (ctx->atlas.id) {
GLuint boundTexture = 0;
glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&boundTexture);
glBindTexture(GL_TEXTURE_2D, ctx->atlas.id);
if (ctx->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);
glBindTexture(GL_TEXTURE_2D, boundTexture);
}
break;
case GLC_HINTING_QSO:
ctx->enableState.hinting = value;
break;
case GLC_EXTRUDE_QSO:
ctx->enableState.extrude = value;
break;
case GLC_KERNING_QSO:
ctx->enableState.kerning = value;
break;
}
}
/** \ingroup context
* This command assigns the value \b GL_FALSE to the boolean variable
* identified by \e inAttrib.
* <center>
* <table>
* <caption>Boolean variables</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td> <td>Initial value</td>
* </tr>
* <tr>
* <td><b>GLC_AUTO_FONT</b></td> <td>0x0010</td> <td><b>GL_TRUE</b></td>
* </tr>
* <tr>
* <td><b>GLC_GL_OBJECTS</b></td> <td>0x0011</td> <td><b>GL_TRUE</b></td>
* </tr>
* <tr>
* <td><b>GLC_MIPMAP</b></td> <td>0x0012</td> <td><b>GL_TRUE</b></td>
* </tr>
* <tr>
* <td><b>GLC_HINTING_QSO</b></td>
* <td>0x8005</td>
* <td><b>GL_FALSE</b></td>
* </tr>
* <tr>
* <td><b>GLC_EXTRUDE_QSO</b></td>
* <td>0x8006</td>
* <td><b>GL_FALSE</b></td>
* </tr>
* <tr>
* <td><b>GLC_KERNING_QSO</b></td>
* <td>0x8007</td>
* <td><b>GL_FALSE</b></td>
* </tr>
* </table>
* </center>
* \param inAttrib A symbolic constant indicating a GLC capability.
* \sa glcIsEnabled()
* \sa glcEnable()
*/
void APIENTRY glcDisable(GLCenum inAttrib)
{
GLC_INIT_THREAD();
__glcChangeState(inAttrib, GL_FALSE);
}
/** \ingroup context
* This command assigns the value \b GL_TRUE to the boolean variable
* identified by \e inAttrib which must be chosen in the table above.
*
* - \b GLC_AUTO_FONT : if enabled, GLC tries to automatically find a font
* among the masters to map the character code to be rendered (see also
* glcRenderChar()).
* - \b GLC_GL_OBJECTS : if enabled, GLC stores characters rendering commands
* in GL display lists and textures in GL texture objects.
* - \b GLC_MIPMAP : if enabled, texture objects used by GLC are mipmapped
* - \b GLC_HINTING_QSO : if enabled, GLC uses the hinting procedures that are
* available for most scalable fonts. It gives better results for characters
* that are rendered at small sizes. This attribute is ignored when
* \b GLC_GL_OBJECTS is enabled. Hinting may generate visual artifacts such
* as "shaking outlines" if the character is animated. This attribute should
* be disabled in such cases.
* - \b GLC_EXTRUDE_QSO : if enabled and \b GLC_RENDER_STYLE is
* \b GLC_TRIANGLE then GLC renders extruded characters with a thickness
* equal to 1.0. A call to glScale3f(1., 1., \e thickness ) can be added
* before the rendering commands in order to obtain the desired thickness.
* - \b GLC_KERNING_QSO : if enabled, GLC uses kerning information when
* rendering or measuring a string. Not all fonts have kerning informations.
*
* \param inAttrib A symbolic constant indicating a GLC attribute.
* \sa glcDisable()
* \sa glcIsEnabled()
*/
void APIENTRY glcEnable(GLCenum inAttrib)
{
GLC_INIT_THREAD();
__glcChangeState(inAttrib, GL_TRUE);
}
/** \ingroup context
* This command returns the value of the callback function variable identified
* by \e inOpcode. Currently, \e inOpcode can only have the value
* \b GLC_OP_glcUnmappedCode. Its initial value and the type signature are
* defined in the table shown in glcCallbackFunc()'s definition.
* \param inOpcode The callback function to be retrieved
* \return The value of the callback function variable
* \sa glcCallbackFunc()
*/
GLCfunc APIENTRY glcGetCallbackFunc(GLCenum inOpcode)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameters */
if (inOpcode != GLC_OP_glcUnmappedCode) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return GLC_NONE;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return GLC_NONE;
}
return ctx->stringState.callback;
}
/** \ingroup context
* This command returns the string at offset \e inIndex from the first element
* in the string list identified by \e inAttrib which must be chosen in the
* table below :
*
* <center>
* <table>
* <caption>String lists</caption>
* <tr>
* <td>Name</td>
* <td>Enumerant</td>
* <td>Initial value</td>
* <td>Element count variable</td>
* </tr>
* <tr>
* <td><b>GLC_CATALOG_LIST</b></td>
* <td>0x0080</td>
* <td>\<empty list\></td>
* <td><b>GLC_CATALOG_COUNT</b></td>
* </tr>
* </table>
* </center>
*
* The command raises a \b GLC_PARAMETER_ERROR if \e inIndex is less than zero
* or is greater than or equal to the value of the list's element count
* variable.
* \param inAttrib The string list attribute
* \param inIndex The index from which to retrieve an element.
* \return The string list element
* \sa glcGetListi()
*/
const GLCchar* APIENTRY glcGetListc(GLCenum inAttrib, GLint inIndex)
{
__GLCcontext *ctx = NULL;
GLCchar8* catalog = NULL;
GLCchar* buffer = NULL;
size_t length = 0;
GLC_INIT_THREAD();
/* Check the parameters */
if (inAttrib != GLC_CATALOG_LIST) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return GLC_NONE;
}
/* NOTE : at this stage we can not verify if inIndex is greater than or equal
* to the last element index. In order to perform such a verification we
* would need to have the current context state but GLC specs tells that we
* should first check the parameters _then_ the current context (section 2.2
* of specs). We are done !
*/
if (inIndex < 0) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return GLC_NONE;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return GLC_NONE;
}
catalog = __glcContextGetCatalogPath(ctx, inIndex);
if (!catalog)
return GLC_NONE;
/* Three remarks have to be made concerning the following code :
* 1. We do not return a pointer that points to the actual location of the
* string in order to prevent the user to modify it. Instead QuesoGLC
* returns a pointer that points to a copy of the requested data.
* 2. File names are not translated from one string format (UCS1, UCS2, ...)
* to another because it is not relevant. We make the assumption that
* the user gave us the file names in the current coding of his/her OS and
* that this coding will not change during the program execution even when
* glcStringType() is called.
* 3. There are few chances that this code is executed in a critical loop
* then there is no need to care about optimization.
*/
/* FIXME: Is the use of strlen() adequate here ?
* What if 'catalog' is encoded in UCS2 format or any other weird format ?
*/
length = strlen((const char*) catalog) + 1;
buffer = __glcContextQueryBuffer(ctx, length * sizeof(char));
if (!buffer)
return GLC_NONE; /* GLC_RESOURCE_ERROR has been raised */
strncpy((char*)buffer, (const char*)catalog, length);
return buffer;
}
/** \ingroup context
* This command returns the integer at offset \e inIndex from the first
* element in the integer list identified by \e inAttrib.
*
* You can choose from the following integer lists, listed below with their
* element count variables :
* <center>
* <table>
* <caption>Integer lists</caption>
* <tr>
* <td>Name</td>
* <td>Enumerant</td>
* <td>Initial value</td>
* <td>Element count variable</td>
* </tr>
* <tr>
* <td><b>GLC_CURRENT_FONT_LIST</b></td>
* <td>0x0090</td>
* <td>\<empty list\></td>
* <td><b>GLC_CURRENT_FONT_COUNT</b></td>
* </tr>
* <tr>
* <td><b>GLC_FONT_LIST</b></td>
* <td>0x0091</td>
* <td>\<empty list\></td>
* <td><b>GLC_FONT_COUNT</b></td>
* </tr>
* <tr>
* <td><b>GLC_LIST_OBJECT_LIST</b></td>
* <td>0x0092</td>
* <td>\<empty list\></td>
* <td><b>GLC_LIST_OBJECT_COUNT</b></td>
* </tr>
* <tr>
* <td><b>GLC_TEXTURE_OBJECT_LIST</b></td>
* <td>0x0093</td>
* <td>\<empty list\></td>
* <td><b>GLC_TEXTURE_OBJECT_COUNT</b></td>
* </tr>
* <tr>
* <td><b>GLC_BUFFER_OBJECT_LIST_QSO</b></td>
* <td>0x800F</td>
* <td>\<empty list\></td>
* <td><b>GLC_BUFFER_OBJECT_COUNT_QSO</b></td>
* </tr>
* </table>
* </center>
*
* The command raises a \b GLC_PARAMETER_ERROR if \e inIndex is less than zero
* or is greater than or equal to the value of the list's element count
* variable.
* \param inAttrib The integer list attribute
* \param inIndex The index from which to retrieve the element.
* \return The element from the integer list.
* \sa glcGetListc()
*/
GLint APIENTRY glcGetListi(GLCenum inAttrib, GLint inIndex)
{
__GLCcontext *ctx = NULL;
FT_ListNode node = NULL;
GLC_INIT_THREAD();
/* Check parameters */
switch(inAttrib) {
case GLC_CURRENT_FONT_LIST:
case GLC_FONT_LIST:
case GLC_LIST_OBJECT_LIST:
case GLC_TEXTURE_OBJECT_LIST:
break;
case GLC_BUFFER_OBJECT_LIST_QSO: /* QuesoGLC extension */
/* This parameter is available only if the corresponding GL extensions are
* supported by the GL driver.
*/
if (GLEW_ARB_vertex_buffer_object || GLEW_ARB_pixel_buffer_object)
break;
else {
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0;
}
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0;
}
/* NOTE : at this stage we can not verify if inIndex is greater than or equal
* to the last element index. In order to perform such a verification we
* would need to have the current context states but GLC specs says that we
* should first check the parameters _then_ the current context (section 2.2
* of specs). We are done !
*/
if (inIndex < 0) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return 0;
}
/* Perform the final part of verifications (the one which needs
* the current context's states) then return the requested value.
*/
switch(inAttrib) {
case GLC_CURRENT_FONT_LIST:
for (node = ctx->currentFontList.head; inIndex && node;
node = node->next, inIndex--);
if (node)
return ((__GLCfont*)node->data)->id;
else
break;
case GLC_FONT_LIST:
for (node = ctx->fontList.head; inIndex && node;
node = node->next, inIndex--);
if (node)
return ((__GLCfont*)node->data)->id;
else
break;
case GLC_LIST_OBJECT_LIST:
/* In order to get the display list name, we have to perform a search
* through the list of display lists of every face descriptor.
*/
for (node = ctx->fontList.head; node; node = node->next) {
__GLCfaceDescriptor* faceDesc =
(__GLCfaceDescriptor*)(((__GLCfont*)(node->data))->faceDesc);
FT_ListNode glyphNode = NULL;
for (glyphNode = faceDesc->glyphList.head; glyphNode;
glyphNode = glyphNode->next) {
__GLCglyph* glyph = (__GLCglyph*)glyphNode;
int count = __glcGlyphGetDisplayListCount(glyph);
if (inIndex < count)
return __glcGlyphGetDisplayList(glyph, inIndex);
else
inIndex -= count;
}
}
break;
case GLC_TEXTURE_OBJECT_LIST:
switch(inIndex) {
/* QuesoGLC uses at most 2 textures : one for immediate mode rendering and
* another one for the texture atlas. That's all. They are virtually
* stored in the following order : texture for immediate mode first, then
* texture atlas.
*/
case 0:
if (ctx->texture.id)
return ctx->texture.id;
/* If the texture for immediate mode does not exist, then the first
* texture is the texture atlas.
* FIXME: if the texture atlas is created first and the texture for
* immediate mode is created after then this algorithm leads to a
* modification of the order which is not satisfying...
*/
if (ctx->atlas.id)
return ctx->atlas.id;
break;
case 1:
if ((ctx->texture.id) && (ctx->atlas.id))
return ctx->atlas.id;
break;
default:
break;
}
break;
case GLC_BUFFER_OBJECT_LIST_QSO: /* QuesoGLC extension */
switch(inIndex) {
/* QuesoGLC uses the following buffer objects :
* - one PBO for immediate texture mode rendering
* - one VBO for the texture atlas.
* - for each glyph (for GLC_LINE and GLC_TRIANGLE rendering modes) :
* -> one VBO to store the nodes
* -> one VBO to store the triangles
* Virtually, the buffer objects are numbered as indicated below :
* 0 : PBO for immediate texture mode rendering
* 1 : VBO for the texture atlas
* 2 and on : VBOs for GLC_LINE and GLC_TRIANGLE rendering modes
* If one of the first 2 PBO/VBO is not existing then the numbering is
* shifted down by 1 (or 2 if both are not existing).
*/
case 0:
if (ctx->texture.bufferObjectID)
return ctx->texture.bufferObjectID;
/* If the PBO for immediate mode does not exist, then the first buffer
* object is the VBO for the texture atlas.
* FIXME: if the texture atlas is created first and the PBO for
* immediate mode is created after then this algorithm leads to a
* modification of the order in which buffer objects are reported which
* is not satisfying...
*/
if (ctx->atlas.bufferObjectID)
return ctx->atlas.bufferObjectID;
break;
case 1:
if ((ctx->texture.bufferObjectID) && (ctx->atlas.bufferObjectID))
return ctx->atlas.bufferObjectID;
break;
default:
break;
}
if (ctx->texture.bufferObjectID)
inIndex--;
if (ctx->atlas.bufferObjectID)
inIndex--;
/* The required index is neither the PBO nor the VBO for the texture atlas.
* In order to get the buffer object name, we have to perform a search
* through the list of buffer objects of every face descriptor.
*/
for (node = ctx->fontList.head; node; node = node->next) {
__GLCfaceDescriptor* faceDesc =
(__GLCfaceDescriptor*)(((__GLCfont*)(node->data))->faceDesc);
FT_ListNode glyphNode = NULL;
for (glyphNode = faceDesc->glyphList.head; glyphNode;
glyphNode = glyphNode->next) {
__GLCglyph* glyph = (__GLCglyph*)glyphNode;
int count = __glcGlyphGetBufferObjectCount(glyph);
if (inIndex < count)
return __glcGlyphGetBufferObject(glyph, inIndex);
else
inIndex -= count;
}
}
break;
}
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0;
}
/** \ingroup context
* This command returns the value of the pointer variable identified by
* \e inAttrib.
* <center>
* <table>
* <caption>Pointer variables</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td> <td>Initial value</td>
* </tr>
* <tr>
* <td><b>GLC_DATA_POINTER</b></td>
* <td>0x00A0</td>
* <td><b>GLC_NONE</b></td>
* </tr>
* </table>
* </center>
* \param inAttrib The pointer category
* \return The pointer
* \sa glcDataPointer()
*/
GLvoid* APIENTRY glcGetPointer(GLCenum inAttrib)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameter */
if (inAttrib != GLC_DATA_POINTER) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return GLC_NONE;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return GLC_NONE;
}
return ctx->stringState.dataPointer;
}
/** \ingroup context
* This command returns the value of the string constant identified by
* \e inAttrib. String constants must be chosen in the table below :
* <center>
* <table>
* <caption>String constants</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td>
* </tr>
* <tr>
* <td><b>GLC_EXTENSIONS</b></td> <td>0x00B0</td>
* </tr>
* <tr>
* <td><b>GLC_RELEASE</b></td> <td>0x00B1</td>
* </tr>
* <tr>
* <td><b>GLC_VENDOR</b></td> <td>0x00B2</td>
* </tr>
* </table>
* </center>
* \param inAttrib The attribute that identifies the string constant
* \return The string constant.
* \sa glcGetf()
* \sa glcGeti()
* \sa glcGetfv()
* \sa glcGetPointer()
*/
const GLCchar* APIENTRY glcGetc(GLCenum inAttrib)
{
static const char* __glcExtensions1 = "GLC_QSO_attrib_stack";
static const char* __glcExtensions2 = " GLC_QSO_buffer_object";
static const char* __glcExtensions3 = " GLC_QSO_extrude GLC_QSO_hinting"
" GLC_QSO_kerning GLC_QSO_matrix_stack GLC_QSO_render_parameter"
" GLC_QSO_render_pixmap GLC_QSO_utf8 GLC_SGI_full_name";
static const GLCchar8* __glcVendor = (const GLCchar8*) "The QuesoGLC Project";
#ifdef HAVE_CONFIG_H
static const GLCchar8* __glcRelease = (const GLCchar8*) PACKAGE_VERSION;
#else
static const GLCchar8* __glcRelease = (const GLCchar8*) QUESOGLC_VERSION;
#endif
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameters */
switch(inAttrib) {
case GLC_EXTENSIONS:
case GLC_RELEASE:
case GLC_VENDOR:
break;
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return GLC_NONE;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return GLC_NONE;
}
/* Translate the string to the relevant Unicode format */
switch(inAttrib) {
case GLC_EXTENSIONS:
{
GLCchar8 __glcExtensions[256];
/* This assertion checks that the fixed sized array __glcExtensions is
* large enough to store the extensions name. If this is not the case
* then the size must be updated.
*/
assert((strlen(__glcExtensions1) + strlen(__glcExtensions2)
+ strlen(__glcExtensions3)) <= 256);
/* Build the extensions string depending on the available GL extensions */
strcpy((char*)__glcExtensions, __glcExtensions1);
if (GLEW_ARB_vertex_buffer_object || GLEW_ARB_pixel_buffer_object)
strcat((char*)__glcExtensions, __glcExtensions2);
strcat((char*)__glcExtensions, __glcExtensions3);
return __glcConvertFromUtf8ToBuffer(ctx, __glcExtensions);
}
case GLC_RELEASE:
return __glcConvertFromUtf8ToBuffer(ctx, __glcRelease);
case GLC_VENDOR:
return __glcConvertFromUtf8ToBuffer(ctx, __glcVendor);
default:
return GLC_NONE;
}
}
/** \ingroup context
* This command returns the value of the floating point variable identified
* by \e inAttrib.
* <center>
* <table>
* <caption>Float point variables</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td> <td>Initial value</td>
* </tr>
* <tr>
* <td><b>GLC_RESOLUTION</b></td> <td>0x00C0</td> <td>0.0</td>
* </tr>
* <tr>
* <td><b>GLC_PARAMETRIC_TOLERANCE_QSO</b></td> <td>0x8010</td>
* <td>0.005</td>
* </tr>
* </table>
* </center>
* \param inAttrib The parameter value to be returned.
* \return The current value of the floating point variable.
* \sa glcGetc()
* \sa glcGeti()
* \sa glcGetfv()
* \sa glcGetPointer()
*/
GLfloat APIENTRY glcGetf(GLCenum inAttrib)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameter */
switch(inAttrib) {
case GLC_RESOLUTION:
case GLC_PARAMETRIC_TOLERANCE_QSO:
break;
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0.f;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return 0.f;
}
switch(inAttrib) {
case GLC_RESOLUTION:
return ctx->renderState.resolution;
case GLC_PARAMETRIC_TOLERANCE_QSO:
return ctx->renderState.tolerance;
}
return 0.f;
}
/** \ingroup context
* This command stores into \e outVec the value of the floating point vector
* identified by \e inAttrib. If the command does not raise an error, it
* returns \e outVec, otherwise it returns a \b NULL value.
* <center>
* <table>
* <caption>Floating point vector variables</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td> <td>Initial value</td>
* </tr>
* <tr>
* <td><b>GLC_BITMAP_MATRIX</b></td> <td>0x00D0</td> <td>[ 1. 0. 0. 1.]</td>
* </tr>
* </table>
* </center>
*
* \param inAttrib The parameter value to be returned
* \param outVec Specifies where to store the return value
* \return The current value of the floating point vector variable
* \sa glcGetf()
* \sa glcGeti()
* \sa glcGetc()
* \sa glcGetPointer()
*/
GLfloat* APIENTRY glcGetfv(GLCenum inAttrib, GLfloat* outVec)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameters */
if (inAttrib != GLC_BITMAP_MATRIX) {
__glcRaiseError(GLC_PARAMETER_ERROR);
return NULL;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return NULL;
}
memcpy(outVec, ctx->bitmapMatrix, 4 * sizeof(GLfloat));
return outVec;
}
/** \ingroup context
* This command returns the value of the integer variable or constant
* identified by \e inAttrib.
* <center>
* <table>
* <caption>Integer variables and constants</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td> <td>Initial value</td>
* </tr>
* <tr>
* <td><b>GLC_CATALOG_COUNT</b></td>
* <td>0x00E0</td>
* <td>\<implementation specific\></td>
* </tr>
* <tr>
* <td><b>GLC_CURRENT_FONT_COUNT</b></td> <td>0x00E1</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_FONT_COUNT</b></td> <td>0x00E2</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_LIST_OBJECT_COUNT</b></td> <td>0x00E3</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_MASTER_COUNT</b></td>
* <td>0x00E4</td>
* <td>\<implementation specific\></td>
* </tr>
* <tr>
* <td><b>GLC_MEASURED_CHAR_COUNT</b></td> <td>0x00E5</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_RENDER_STYLE</b></td>
* <td>0x00E6</td>
* <td><b>GLC_BITMAP</b></td>
* </tr>
* <tr>
* <td><b>GLC_REPLACEMENT_CODE</b></td> <td>0x00E7</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_STRING_TYPE</b></td> <td>0x00E8</td> <td><b>GLC_UCS1</b></td>
* </tr>
* <tr>
* <td><b>GLC_TEXTURE_OBJECT_COUNT</b></td> <td>0x00E9</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_VERSION_MAJOR</b></td>
* <td>0x00EA</td>
* <td>\<implementation specific\></td>
* </tr>
* <tr>
* <td><b>GLC_VERSION_MINOR</b></td>
* <td>0x00EB</td>
* <td>\<implementation specific\></td>
* </tr>
* <tr>
* <td><b>GLC_MATRIX_STACK_DEPTH_QSO</b></td> <td>0x8008</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_MAX_MATRIX_STACK_DEPTH_QSO</b></td>
* <td>0x8009</td>
* <td>\<implementation specific\></td>
* </tr>
* <tr>
* <td><b>GLC_ATTRIB_STACK_DEPTH_QSO</b></td> <td>0x800C</td> <td>0</td>
* </tr>
* <tr>
* <td><b>GLC_MAX_ATTRIB_STACK_DEPTH_QSO</b></td>
* <td>0x800D</td>
* <td>\<implementation specific\></td>
* </tr>
* <tr>
* <td><b>GLC_BUFFER_OBJECT_COUNT_QSO</b></td> <td>0x800E</td> <td>0</td>
* </tr>
* </table>
* </center>
* \param inAttrib Attribute for which an integer variable is requested.
* \return The value or values of the integer variable.
* \sa glcGetc()
* \sa glcGetf()
* \sa glcGetfv()
* \sa glcGetPointer()
*/
GLint APIENTRY glcGeti(GLCenum inAttrib)
{
__GLCcontext *ctx = NULL;
FT_ListNode node = NULL;
GLint count = 0;
GLC_INIT_THREAD();
/* Check the parameters */
switch(inAttrib) {
case GLC_CATALOG_COUNT:
case GLC_CURRENT_FONT_COUNT:
case GLC_FONT_COUNT:
case GLC_LIST_OBJECT_COUNT:
case GLC_MASTER_COUNT:
case GLC_MEASURED_CHAR_COUNT:
case GLC_RENDER_STYLE:
case GLC_REPLACEMENT_CODE:
case GLC_STRING_TYPE:
case GLC_TEXTURE_OBJECT_COUNT:
case GLC_VERSION_MAJOR:
case GLC_VERSION_MINOR:
case GLC_MATRIX_STACK_DEPTH_QSO: /* QuesoGLC extension */
case GLC_MAX_MATRIX_STACK_DEPTH_QSO: /* QuesoGLC extension */
case GLC_ATTRIB_STACK_DEPTH_QSO: /* QuesoGLC extension */
case GLC_MAX_ATTRIB_STACK_DEPTH_QSO: /* QuesoGLC extension */
break;
case GLC_BUFFER_OBJECT_COUNT_QSO: /* QuesoGLC extension */
/* This parameter is available only if the corresponding GL extensions are
* supported by the GL driver.
*/
if (GLEW_ARB_vertex_buffer_object || GLEW_ARB_pixel_buffer_object)
break;
else {
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0;
}
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return 0;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return 0;
}
/* Returns the requested value */
switch(inAttrib) {
case GLC_CATALOG_COUNT:
return GLC_ARRAY_LENGTH(ctx->catalogList);
case GLC_CURRENT_FONT_COUNT:
for (node = ctx->currentFontList.head, count = 0; node;
node = node->next, count++);
return count;
case GLC_FONT_COUNT:
for (count = 0, node = ctx->fontList.head; node;
node = node->next, count++);
return count;
case GLC_LIST_OBJECT_COUNT:
for (node = ctx->fontList.head; node; node = node->next) {
__GLCfaceDescriptor* faceDesc =
(__GLCfaceDescriptor*)(((__GLCfont*)(node->data))->faceDesc);
FT_ListNode glyphNode = NULL;
for (glyphNode = faceDesc->glyphList.head; glyphNode;
glyphNode = glyphNode->next) {
__GLCglyph* glyph = (__GLCglyph*)glyphNode;
count += __glcGlyphGetDisplayListCount(glyph);
}
}
return count;
case GLC_MASTER_COUNT:
return GLC_ARRAY_LENGTH(ctx->masterHashTable);
case GLC_MEASURED_CHAR_COUNT:
return GLC_ARRAY_LENGTH(ctx->measurementBuffer);
case GLC_RENDER_STYLE:
return ctx->renderState.renderStyle;
case GLC_REPLACEMENT_CODE:
/* Return the replacement character converted to the current string type */
return __glcConvertUcs4ToGLint(ctx, ctx->stringState.replacementCode);
case GLC_STRING_TYPE:
return ctx->stringState.stringType;
case GLC_TEXTURE_OBJECT_COUNT:
count += (ctx->texture.id ? 1 : 0);
count += (ctx->atlas.id ? 1 : 0);
return count;
case GLC_VERSION_MAJOR:
return __glcCommonArea.versionMajor;
case GLC_VERSION_MINOR:
return __glcCommonArea.versionMinor;
case GLC_MATRIX_STACK_DEPTH_QSO: /* QuesoGLC extension */
return ctx->bitmapMatrixStackDepth;
case GLC_MAX_MATRIX_STACK_DEPTH_QSO: /* QuesoGLC extension */
return GLC_MAX_MATRIX_STACK_DEPTH;
case GLC_ATTRIB_STACK_DEPTH_QSO: /* QuesoGLC extension */
return ctx->attribStackDepth;
case GLC_MAX_ATTRIB_STACK_DEPTH_QSO: /* QuesoGLC extension */
return GLC_MAX_ATTRIB_STACK_DEPTH;
case GLC_BUFFER_OBJECT_COUNT_QSO: /* QuesoGLC extension */
count += (ctx->texture.bufferObjectID ? 1 : 0);
count += (ctx->atlas.bufferObjectID ? 1 : 0);
for (node = ctx->fontList.head; node; node = node->next) {
__GLCfaceDescriptor* faceDesc =
(__GLCfaceDescriptor*)(((__GLCfont*)(node->data))->faceDesc);
FT_ListNode glyphNode = NULL;
for (glyphNode = faceDesc->glyphList.head; glyphNode;
glyphNode = glyphNode->next) {
__GLCglyph* glyph = (__GLCglyph*)glyphNode;
count += __glcGlyphGetBufferObjectCount(glyph);
}
}
return count;
}
return 0;
}
/** \ingroup context
* This command returns \b GL_TRUE if the value of the boolean variable
* identified by inAttrib is \b GL_TRUE <em>(quoted from the specs ^_^)</em>
*
* Attributes that can be enabled or disabled are listed on the glcDisable()
* description.
* \param inAttrib The attribute to be tested
* \return The state of the attribute \e inAttrib.
* \sa glcEnable()
* \sa glcDisable()
*/
GLboolean APIENTRY glcIsEnabled(GLCenum inAttrib)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameters */
switch(inAttrib) {
case GLC_AUTO_FONT:
case GLC_GL_OBJECTS:
case GLC_MIPMAP:
case GLC_HINTING_QSO: /* QuesoGLC Extension */
case GLC_EXTRUDE_QSO: /* QuesoGLC Extension */
case GLC_KERNING_QSO: /* QuesoGLC Extension */
break;
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return GL_FALSE;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return GL_FALSE;
}
/* Returns the requested value */
switch(inAttrib) {
case GLC_AUTO_FONT:
return ctx->enableState.autoFont;
case GLC_GL_OBJECTS:
return ctx->enableState.glObjects;
case GLC_MIPMAP:
return ctx->enableState.mipmap;
case GLC_HINTING_QSO: /* QuesoGLC Extension */
return ctx->enableState.hinting;
case GLC_EXTRUDE_QSO: /* QuesoGLC Extension */
return ctx->enableState.extrude;
case GLC_KERNING_QSO: /* QuesoGLC Extension */
return ctx->enableState.kerning;
}
return GL_FALSE;
}
/** \ingroup context
* This command assigns the value \e inStringType to the variable
* \b GLC_STRING_TYPE. The string types are listed in the table
* below :
* <center>
* <table>
* <caption>String types</caption>
* <tr>
* <td>Name</td> <td>Enumerant</td> <td>Type of characters</td>
* </tr>
* <tr>
* <td><b>GLC_UCS1</b></td> <td>0x0110</td> <td>GLubyte</td>
* </tr>
* <tr>
* <td><b>GLC_UCS2</b></td> <td>0x0111</td> <td>GLushort</td>
* </tr>
* <tr>
* <td><b>GLC_UCS4</b></td> <td>0x0112</td> <td>GLuint</td>
* </tr>
* <tr>
* <td><b>GLC_UTF8_QSO</b></td>
* <td>0x8004</td>
* <td>\<character dependent\></td>
* </tr>
* </table>
* </center>
*
* Every character string used in the GLC API is represented as a
* zero-terminated array, unless otherwise specified. The value of the
* variable \b GLC_STRING_TYPE determines the interpretation of the array. The
* values \b GLC_UCS1, \b GLC_UCS2, \b GLC_UCS4 and \b GLC_UTF8_QSO indicate
* how each element of the string should be interpreted. Currently QuesoGLC
* supports UCS1, UCS2, UCS4 and UTF-8 formats as defined in the Unicode 4.0.1
* and ISO/IEC 10646:2003 standards. The initial value of \b GLC_STRING_TYPE
* is \b GLC_UCS1.
*
* \note Currently, the string formats UCS2 and UCS4 are interpreted according
* to the underlying platform endianess. If the strings are provided in a
* different endianess than the platform's, the client must translate the
* strings in the correct endianess.
*
* The value of a character code in a returned string may exceed the range
* of the character encoding selected by \b GLC_STRING_TYPE. In this case,
* the returned character is converted to a 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 original character code
* represented as a sequence of hexadecimal digits. The sequence has no
* leading zeros, and alphabetic digits are in upper case.
* \param inStringType Value to assign to \b GLC_STRING_TYPE
* \sa glcGeti() with argument \b GLC_STRING_TYPE
*/
void APIENTRY glcStringType(GLCenum inStringType)
{
__GLCcontext *ctx = NULL;
GLC_INIT_THREAD();
/* Check the parameters */
switch(inStringType) {
case GLC_UCS1:
case GLC_UCS2:
case GLC_UCS4:
case GLC_UTF8_QSO: /* QuesoGLC Extension */
break;
default:
__glcRaiseError(GLC_PARAMETER_ERROR);
return;
}
/* Check if the thread has a current context */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return;
}
ctx->stringState.stringType = inStringType;
return;
}
/** \ingroup context
* This command provides a means to save groups of state variables. It takes a
* OR of symbolic constants indicating which groups of state variables to push
* onto the attribute stack. Each constant refers to a group of state
* variables.
* <center>
* <table>
* <caption>Group attributes</caption>
* <tr>
* <td>Group attribute</td> <td>Name</td> <td>Enumerant</td>
* </tr>
* <tr>
* <td>enable</td> <td><b>GLC_ENABLE_BIT_QSO</b></td> <td>0x0001</td>
* </tr>
* <tr>
* <td>render</td> <td><b>GLC_RENDER_BIT_QSO</b></td> <td>0x0002</td>
* </tr>
* <tr>
* <td>string</td> <td><b>GLC_STRING_BIT_QSO</b></td> <td>0x0004</td>
* </tr>
* <tr>
* <td> </td> <td><b>GLC_GL_ATTRIB_BIT_QSO</b></td> <td>0x0008</td>
* </tr>
* <tr>
* <td> </td> <td><b>GLC_ALL_ATTRIBS_BIT_QSO</b></td> <td>0xFFFF</td>
* </tr>
* </table>
* </center>
*
* The classification of each variable into a group is indicated in the
* following table of state variables.
* <center>
* <table>
* <caption>State variables</caption>
* <tr>
* <td>Name</td> <td>Type</td> <td>Get command</td> <td>Group attribute</td>
* </tr>
* <tr>
* <td><b>GLC_AUTO_FONT</b></td>
* <td>GLboolean</td>
* <td>glcIsEnabled()</td>
* <td>enable</td>
* </tr>
* <tr>
* <td><b>GLC_GL_OBJECTS</b></td>
* <td>GLboolean</td>
* <td>glcIsEnabled()</td>
* <td>enable</td>
* </tr>
* <tr>
* <td><b>GLC_MIPMAP</b></td>
* <td>GLboolean</td>
* <td>glcIsEnabled()</td>
* <td>enable</td>
* </tr>
* <tr>
* <td><b>GLC_HINTING_QSO</b></td>
* <td>GLboolean</td>
* <td>glcIsEnabled()</td>
* <td>enable</td>
* </tr>
* <tr>
* <td><b>GLC_EXTRUDE_QSO</b></td>
* <td>GLboolean</td>
* <td>glcIsEnabled()</td>
* <td>enable</td>
* </tr>
* <tr>
* <td><b>GLC_KERNING_QSO</b></td>
* <td>GLboolean</td>
* <td>glcIsEnabled()</td>
* <td>enable</td>
* </tr>
* <tr>
* <td><b>GLC_RENDER_STYLE</b></td>
* <td>GLint</td>
* <td>glcGeti()</td>
* <td>render</td>
* </tr>
* <tr>
* <td><b>GLC_RESOLUTION</b></td>
* <td>GLfloat</td>
* <td>glcGetf()</td>
* <td>render</td>
* </tr>
* <tr>
* <td><b>GLC_STRING_TYPE</b></td>
* <td>GLint</td>
* <td>glcGeti()</td>
* <td>string</td>
* </tr>
* <tr>
* <td><b>GLC_REPLACEMENT_CODE</b></td>
* <td>GLint</td>
* <td>glcGeti()</td>
* <td>string</td>
* </tr>
* <tr>
* <td><b>GLC_OP_glcUnmappedCode</b></td>
* <td>GLCfunc</td>
* <td>glcGetCallbackFunc()</td>
* <td>string</td>
* </tr>
* <tr>
* <td><b>GLC_DATA_POINTER</b></td>
* <td>GLvoid*</td>
* <td>glcGetPointer()</td>
* <td>string</td>
* </tr>
* </table>
* </center>
*
* The error \b GLC_STACK_OVERFLOW_QSO is generated if glcPushAttribQSO() is
* executed while the attribute stack depth is equal to
* \b GLC_MAX_ATTRIB_STACK_DEPTH_QSO.
* \b GLC_STACK_OVERFLOW_QSO.
* \param inMask The list of state variables to be saved
* \sa glcPopAttribQSO()
* \sa glcGeti() with argument \b GLC_ATTRIB_STACK_DEPTH_QSO
* \sa glcGeti() with argument \b GLC_MAX_ATTRIB_STACK_DEPTH_QSO
*/
void APIENTRY glcPushAttribQSO(GLbitfield inMask)
{
__GLCcontext *ctx = NULL;
__GLCattribStackLevel *level = 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;
}
/* Check if there is enough room remaining in the stack */
if (ctx->attribStackDepth >= GLC_MAX_ATTRIB_STACK_DEPTH) {
__glcRaiseError(GLC_STACK_OVERFLOW_QSO);
return;
}
/* Get a level from the stack and initialise it.
* The stack level is incremented.
*/
level = &ctx->attribStack[ctx->attribStackDepth++];
level->attribBits = 0;
/* Save the state in the stack level with respect to the bitfield mask */
if (inMask & GLC_ENABLE_BIT_QSO) {
memcpy(&level->enableState, &ctx->enableState, sizeof(__GLCenableState));
level->attribBits |= GLC_ENABLE_BIT_QSO;
}
if (inMask & GLC_RENDER_BIT_QSO) {
memcpy(&level->renderState, &ctx->renderState, sizeof(__GLCrenderState));
level->attribBits |= GLC_RENDER_BIT_QSO;
}
if (inMask & GLC_STRING_BIT_QSO) {
memcpy(&level->stringState, &ctx->stringState, sizeof(__GLCstringState));
level->attribBits |= GLC_STRING_BIT_QSO;
}
if (inMask & GLC_GL_ATTRIB_BIT_QSO) {
__glcSaveGLState(&level->glState, ctx, GL_TRUE);
level->attribBits |= GLC_GL_ATTRIB_BIT_QSO;
}
return;
}
/** \ingroup context
* This command resets the values of those state variables that were saved with
* the last corresponding glcPushAttribQSO(). Those not saved remain unchanged.
* The error \b GLC_STACK_UNDERFLOW_QSO is generated if glcPopAttrib() is
* executed while the attribute stack is empty.
* \sa glcPushAttribQSO()
* \sa glcGeti() with argument \b GLC_ATTRIB_STACK_DEPTH_QSO
* \sa glcGeti() with argument \b GLC_MAX_ATTRIB_STACK_DEPTH_QSO
*/
void APIENTRY glcPopAttribQSO(void)
{
__GLCcontext *ctx = NULL;
__GLCattribStackLevel *level = NULL;
GLbitfield mask;
GLC_INIT_THREAD();
/* Check if the current thread owns a context state */
ctx = GLC_GET_CURRENT_CONTEXT();
if (!ctx) {
__glcRaiseError(GLC_STATE_ERROR);
return;
}
/* Check that the stack contains saved context states */
if (ctx->attribStackDepth <= 0) {
__glcRaiseError(GLC_STACK_UNDERFLOW_QSO);
return;
}
/* Get the last context state saved in the stack.
* The stack depth is decremented.
*/
level = &ctx->attribStack[--ctx->attribStackDepth];
mask = level->attribBits;
/* Restore the context state with respect to the bitfield mask */
if (mask & GLC_ENABLE_BIT_QSO)
memcpy(&ctx->enableState, &level->enableState, sizeof(__GLCenableState));
if (mask & GLC_RENDER_BIT_QSO)
memcpy(&ctx->renderState, &level->renderState, sizeof(__GLCrenderState));
if (mask & GLC_STRING_BIT_QSO)
memcpy(&ctx->stringState, &level->stringState, sizeof(__GLCstringState));
if (mask & GLC_GL_ATTRIB_BIT_QSO)
__glcRestoreGLState(&level->glState, ctx, GL_TRUE);
return;
}