/**************************************************************************** * * Mesa 3-D graphics library * Direct3D Driver Interface * * ======================================================================== * * Copyright (C) 1991-2004 SciTech Software, Inc. 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 * SCITECH SOFTWARE INC 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. * * ====================================================================== * * Language: ANSI C * Environment: Windows 9x (Win32) * * Description: OpenGL window functions (wgl*). * ****************************************************************************/ #include "dglwgl.h" #ifdef _USE_GLD3_WGL #include "gld_driver.h" #endif #include "gl/glu.h" // MUST USE MICROSOFT'S GLU32! #ifndef _USE_GLD3_WGL extern DGL_mesaFuncs mesaFuncs; #endif // Need to export wgl* functions if using GLD3, // otherwise export GLD2 DGL_* functions. #ifdef _USE_GLD3_WGL #define _GLD_WGL_EXPORT(a) wgl##a #else #define _GLD_WGL_EXPORT(a) DGL_##a #endif // Calls into Mesa 4.x are different #ifdef _USE_GLD3_WGL #include "dlist.h" #include "drawpix.h" #include "get.h" #include "matrix.h" // NOTE: All the _GLD* macros now call the gl* functions direct. // This ensures that the correct internal pathway is taken. KeithH #define _GLD_glNewList glNewList #define _GLD_glBitmap glBitmap #define _GLD_glEndList glEndList #define _GLD_glDeleteLists glDeleteLists #define _GLD_glGetError glGetError #define _GLD_glTranslatef glTranslatef #define _GLD_glBegin glBegin #define _GLD_glVertex2fv glVertex2fv #define _GLD_glEnd glEnd #define _GLD_glNormal3f glNormal3f #define _GLD_glVertex3f glVertex3f #define _GLD_glVertex3fv glVertex3fv #else // _USE_GLD3_WGL #define _GLD_glNewList (*mesaFuncs.glNewList) #define _GLD_glBitmap (*mesaFuncs.glBitmap) #define _GLD_glEndList (*mesaFuncs.glEndList) #define _GLD_glDeleteLists (*mesaFuncs.glDeleteLists) #define _GLD_glGetError (*mesaFuncs.glGetError) #define _GLD_glTranslatef (*mesaFuncs.glTranslatef) #define _GLD_glBegin (*mesaFuncs.glBegin) #define _GLD_glVertex2fv (*mesaFuncs.glVertex2fv) #define _GLD_glEnd (*mesaFuncs.glEnd) #define _GLD_glNormal3f (*mesaFuncs.glNormal3f) #define _GLD_glVertex3f (*mesaFuncs.glVertex3f) #define _GLD_glVertex3fv (*mesaFuncs.glVertex3fv) #endif // _USE_GLD3_WGL // *********************************************************************** // Emulate SGI DDK calls. #define __wglMalloc(a) GlobalAlloc(GPTR, (a)) #define __wglFree(a) GlobalFree((a)) // *********************************************************************** // Mesa glu.h and MS glu.h call these different things... //#define GLUtesselator GLUtriangulatorObj //#define GLU_TESS_VERTEX_DATA GLU_VERTEX_DATA // For wglFontOutlines typedef GLUtesselator *(APIENTRY *gluNewTessProto)(void); typedef void (APIENTRY *gluDeleteTessProto)(GLUtesselator *tess); typedef void (APIENTRY *gluTessBeginPolygonProto)(GLUtesselator *tess, void *polygon_data); typedef void (APIENTRY *gluTessBeginContourProto)(GLUtesselator *tess); typedef void (APIENTRY *gluTessVertexProto)(GLUtesselator *tess, GLdouble coords[3], void *data); typedef void (APIENTRY *gluTessEndContourProto)(GLUtesselator *tess); typedef void (APIENTRY *gluTessEndPolygonProto)(GLUtesselator *tess); typedef void (APIENTRY *gluTessPropertyProto)(GLUtesselator *tess, GLenum which, GLdouble value); typedef void (APIENTRY *gluTessNormalProto)(GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z); typedef void (APIENTRY *gluTessCallbackProto)(GLUtesselator *tess, GLenum which, void (CALLBACK *)()); static HINSTANCE gluModuleHandle; static gluNewTessProto gluNewTessProc; static gluDeleteTessProto gluDeleteTessProc; static gluTessBeginPolygonProto gluTessBeginPolygonProc; static gluTessBeginContourProto gluTessBeginContourProc; static gluTessVertexProto gluTessVertexProc; static gluTessEndContourProto gluTessEndContourProc; static gluTessEndPolygonProto gluTessEndPolygonProc; static gluTessPropertyProto gluTessPropertyProc; static gluTessNormalProto gluTessNormalProc; static gluTessCallbackProto gluTessCallbackProc; static HFONT hNewFont, hOldFont; static FLOAT ScaleFactor; #define LINE_BUF_QUANT 4000 #define VERT_BUF_QUANT 4000 static FLOAT* LineBuf; static DWORD LineBufSize; static DWORD LineBufIndex; static FLOAT* VertBuf; static DWORD VertBufSize; static DWORD VertBufIndex; static GLenum TessErrorOccurred; static int AppendToLineBuf( FLOAT value); static int AppendToVertBuf( FLOAT value); static int DrawGlyph( UCHAR* glyphBuf, DWORD glyphSize, FLOAT chordalDeviation, FLOAT extrusion, INT format); static void FreeLineBuf(void); static void FreeVertBuf(void); static long GetWord( UCHAR** p); static long GetDWord( UCHAR** p); static double GetFixed( UCHAR** p); static int InitLineBuf(void); static int InitVertBuf(void); static HFONT CreateHighResolutionFont( HDC hDC); static int MakeDisplayListFromGlyph( DWORD listName, UCHAR* glyphBuf, DWORD glyphSize, LPGLYPHMETRICSFLOAT glyphMetricsFloat, FLOAT chordalDeviation, FLOAT extrusion, INT format); static BOOL LoadGLUTesselator(void); static BOOL UnloadGLUTesselator(void); static int MakeLinesFromArc( FLOAT x0, FLOAT y0, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, DWORD vertexCountIndex, FLOAT chordalDeviationSquared); static int MakeLinesFromGlyph( UCHAR* glyphBuf, DWORD glyphSize, FLOAT chordalDeviation); static int MakeLinesFromTTLine( UCHAR** pp, DWORD vertexCountIndex, WORD pointCount); static int MakeLinesFromTTPolycurve( UCHAR** pp, DWORD vertexCountIndex, FLOAT chordalDeviation); static int MakeLinesFromTTPolygon( UCHAR** pp, FLOAT chordalDeviation); static int MakeLinesFromTTQSpline( UCHAR** pp, DWORD vertexCountIndex, WORD pointCount, FLOAT chordalDeviation); static void CALLBACK TessCombine( double coords[3], void* vertex_data[4], FLOAT weight[4], void** outData); static void CALLBACK TessError( GLenum error); static void CALLBACK TessVertexOutData( FLOAT p[3], GLfloat z); // *********************************************************************** #ifdef GLD_THREADS #pragma message("compiling DGLWGL.C vars for multi-threaded support") extern CRITICAL_SECTION CriticalSection; extern DWORD dwTLSPixelFormat; // TLS index for current pixel format #endif int curPFD = 0; // Current PFD (static) // *********************************************************************** int dglGetPixelFormat(void) { #ifdef GLD_THREADS int iPixelFormat; // get thread-specific instance if (glb.bMultiThreaded) { __try { iPixelFormat = (int)TlsGetValue(dwTLSPixelFormat); } __except(EXCEPTION_EXECUTE_HANDLER) { iPixelFormat = curPFD; } } // get global static var else { iPixelFormat = curPFD; } return iPixelFormat; #else return curPFD; #endif } // *********************************************************************** void dglSetPixelFormat(int iPixelFormat) { #ifdef GLD_THREADS // set thread-specific instance if (glb.bMultiThreaded) { __try { TlsSetValue(dwTLSPixelFormat, (LPVOID)iPixelFormat); } __except(EXCEPTION_EXECUTE_HANDLER) { curPFD = iPixelFormat; } } // set global static var else { curPFD = iPixelFormat; } #else curPFD = iPixelFormat; #endif } // *********************************************************************** int APIENTRY _GLD_WGL_EXPORT(ChoosePixelFormat)( HDC a, CONST PIXELFORMATDESCRIPTOR *ppfd) { DGL_pixelFormat *lpPF = glb.lpPF; PIXELFORMATDESCRIPTOR ppfdBest; int i; int bestIndex = -1; int numPixelFormats; DWORD dwFlags; char buf[128]; char cat[8]; DWORD dwAllFlags = PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI | PFD_SUPPORT_OPENGL | PFD_GENERIC_FORMAT | PFD_NEED_PALETTE | PFD_NEED_SYSTEM_PALETTE | PFD_DOUBLEBUFFER | PFD_STEREO | /*PFD_SWAP_LAYER_BUFFERS |*/ PFD_DOUBLEBUFFER_DONTCARE | PFD_STEREO_DONTCARE | PFD_SWAP_COPY | PFD_SWAP_EXCHANGE | PFD_GENERIC_ACCELERATED | 0; // Validate license if (!dglValidate()) return 0; // List may not be built until dglValidate() is called! KeithH lpPF = glb.lpPF; // // Lets print the input pixel format to the log // ** Based on "wglinfo" by Nate Robins ** // ddlogMessage(DDLOG_SYSTEM, "ChoosePixelFormat:\n"); ddlogMessage(DDLOG_INFO, "Input pixel format for ChoosePixelFormat:\n"); ddlogMessage(DDLOG_INFO, " visual x bf lv rg d st r g b a ax dp st accum buffs ms\n"); ddlogMessage(DDLOG_INFO, " id dep cl sp sz l ci b ro sz sz sz sz bf th cl r g b a ns b\n"); ddlogMessage(DDLOG_INFO, "-----------------------------------------------------------------\n"); sprintf(buf, " . "); sprintf(cat, "%2d ", ppfd->cColorBits); strcat(buf, cat); if(ppfd->dwFlags & PFD_DRAW_TO_WINDOW) sprintf(cat, "wn "); else if(ppfd->dwFlags & PFD_DRAW_TO_BITMAP) sprintf(cat, "bm "); else sprintf(cat, ". "); strcat(buf, cat); /* should find transparent pixel from LAYERPLANEDESCRIPTOR */ sprintf(cat, " . "); strcat(buf, cat); sprintf(cat, "%2d ", ppfd->cColorBits); strcat(buf, cat); /* bReserved field indicates number of over/underlays */ if(ppfd->bReserved) sprintf(cat, " %d ", ppfd->bReserved); else sprintf(cat, " . "); strcat(buf, cat); sprintf(cat, " %c ", ppfd->iPixelType == PFD_TYPE_RGBA ? 'r' : 'c'); strcat(buf, cat); sprintf(cat, "%c ", ppfd->dwFlags & PFD_DOUBLEBUFFER ? 'y' : '.'); strcat(buf, cat); sprintf(cat, " %c ", ppfd->dwFlags & PFD_STEREO ? 'y' : '.'); strcat(buf, cat); if(ppfd->cRedBits && ppfd->iPixelType == PFD_TYPE_RGBA) sprintf(cat, "%2d ", ppfd->cRedBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cGreenBits && ppfd->iPixelType == PFD_TYPE_RGBA) sprintf(cat, "%2d ", ppfd->cGreenBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cBlueBits && ppfd->iPixelType == PFD_TYPE_RGBA) sprintf(cat, "%2d ", ppfd->cBlueBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cAlphaBits && ppfd->iPixelType == PFD_TYPE_RGBA) sprintf(cat, "%2d ", ppfd->cAlphaBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cAuxBuffers) sprintf(cat, "%2d ", ppfd->cAuxBuffers); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cDepthBits) sprintf(cat, "%2d ", ppfd->cDepthBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cStencilBits) sprintf(cat, "%2d ", ppfd->cStencilBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cAccumRedBits) sprintf(cat, "%2d ", ppfd->cAccumRedBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cAccumGreenBits) sprintf(cat, "%2d ", ppfd->cAccumGreenBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cAccumBlueBits) sprintf(cat, "%2d ", ppfd->cAccumBlueBits); else sprintf(cat, " . "); strcat(buf, cat); if(ppfd->cAccumAlphaBits) sprintf(cat, "%2d ", ppfd->cAccumAlphaBits); else sprintf(cat, " . "); strcat(buf, cat); /* no multisample in Win32 */ sprintf(cat, " . .\n"); strcat(buf, cat); ddlogMessage(DDLOG_INFO, buf); ddlogMessage(DDLOG_INFO, "-----------------------------------------------------------------\n"); ddlogMessage(DDLOG_INFO, "\n"); // // Examine the flags for correctness // dwFlags = ppfd->dwFlags; if (dwFlags != (dwFlags & dwAllFlags)) { /* error: bad dwFlags */ ddlogPrintf(DDLOG_WARN, "ChoosePixelFormat: bad flags (0x%x)", dwFlags & (~dwAllFlags)); // Mask illegal flags and continue dwFlags = dwFlags & dwAllFlags; } switch (ppfd->iPixelType) { case PFD_TYPE_RGBA: case PFD_TYPE_COLORINDEX: break; default: /* error: bad iPixelType */ ddlogMessage(DDLOG_WARN, "ChoosePixelFormat: bad pixel type\n"); return 0; } switch (ppfd->iLayerType) { case PFD_MAIN_PLANE: case PFD_OVERLAY_PLANE: case PFD_UNDERLAY_PLANE: break; default: /* error: bad iLayerType */ ddlogMessage(DDLOG_WARN, "ChoosePixelFormat: bad layer type\n"); return 0; } numPixelFormats = glb.nPixelFormatCount; /* loop through candidate pixel format descriptors */ for (i=0; iiPixelType != ppfdCandidate.iPixelType) { continue; } if (ppfd->iLayerType != ppfdCandidate.iLayerType) { continue; } if (((dwFlags ^ ppfdCandidate.dwFlags) & dwFlags) & (PFD_DRAW_TO_WINDOW | PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI | PFD_SUPPORT_OPENGL)) { continue; } if (!(dwFlags & PFD_DOUBLEBUFFER_DONTCARE)) { if ((dwFlags & PFD_DOUBLEBUFFER) != (ppfdCandidate.dwFlags & PFD_DOUBLEBUFFER)) { continue; } } // if (!(dwFlags & PFD_STEREO_DONTCARE)) { if ((dwFlags & PFD_STEREO) != (ppfdCandidate.dwFlags & PFD_STEREO)) { continue; } // } if (ppfd->iPixelType==PFD_TYPE_RGBA && ppfd->cAlphaBits && !ppfdCandidate.cAlphaBits) { continue; } if (ppfd->iPixelType==PFD_TYPE_RGBA && ppfd->cAccumBits && !ppfdCandidate.cAccumBits) { continue; } if (ppfd->cDepthBits && !ppfdCandidate.cDepthBits) { continue; } if (ppfd->cStencilBits && !ppfdCandidate.cStencilBits) { continue; } if (ppfd->cAuxBuffers && !ppfdCandidate.cAuxBuffers) { continue; } /* ** See if candidate is better than the previous best choice */ if (bestIndex == -1) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } if ((ppfd->cColorBits > ppfdBest.cColorBits && ppfdCandidate.cColorBits > ppfdBest.cColorBits) || (ppfd->cColorBits <= ppfdCandidate.cColorBits && ppfdCandidate.cColorBits < ppfdBest.cColorBits)) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } if (ppfd->iPixelType==PFD_TYPE_RGBA && ppfd->cAlphaBits && ppfdCandidate.cAlphaBits > ppfdBest.cAlphaBits) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } if (ppfd->iPixelType==PFD_TYPE_RGBA && ppfd->cAccumBits && ppfdCandidate.cAccumBits > ppfdBest.cAccumBits) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } if ((ppfd->cDepthBits > ppfdBest.cDepthBits && ppfdCandidate.cDepthBits > ppfdBest.cDepthBits) || (ppfd->cDepthBits <= ppfdCandidate.cDepthBits && ppfdCandidate.cDepthBits < ppfdBest.cDepthBits)) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } if (ppfd->cStencilBits && ppfdCandidate.cStencilBits > ppfdBest.cStencilBits) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } if (ppfd->cAuxBuffers && ppfdCandidate.cAuxBuffers > ppfdBest.cAuxBuffers) { ppfdBest = ppfdCandidate; bestIndex = i; continue; } } if (bestIndex != -1) { ddlogPrintf(DDLOG_SYSTEM, "Pixel Format %d chosen as best match", bestIndex+1); return bestIndex + 1; } // Return the pixelformat that has the most capabilities. // ** NOTE: This is only possible due to the way the list // of pixelformats is built. ** // Now picks best pixelformat. KeithH bestIndex = numPixelFormats; // most capable double buffer format ddlogPrintf(DDLOG_SYSTEM, "Pixel Format %d chosen by default", bestIndex); return (bestIndex); } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(CopyContext)( HGLRC a, HGLRC b, UINT c) { // Validate license if (!dglValidate()) return FALSE; UNSUPPORTED("wglCopyContext") return FALSE; // Failed } // *********************************************************************** HGLRC APIENTRY _GLD_WGL_EXPORT(CreateContext)( HDC a) { int ipf; // Validate license if (!dglValidate()) return 0; // Check that the current PFD is valid ipf = dglGetPixelFormat(); if (!IsValidPFD(ipf)) return (HGLRC)0; return dglCreateContext(a, &glb.lpPF[ipf-1]); } // *********************************************************************** HGLRC APIENTRY _GLD_WGL_EXPORT(CreateLayerContext)( HDC a, int b) { // Validate license if (!dglValidate()) return 0; UNSUPPORTED("wglCreateLayerContext") return NULL; // Failed } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(DeleteContext)( HGLRC a) { // Validate license if (!dglValidate()) return FALSE; return dglDeleteContext(a); } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(DescribeLayerPlane)( HDC hDC, int iPixelFormat, int iLayerPlane, UINT nBytes, LPLAYERPLANEDESCRIPTOR plpd) { // Validate license if (!dglValidate()) return FALSE; UNSUPPORTED("DGL_DescribeLayerPlane") // gldLogPrintf(GLDLOG_INFO, "DescribeLayerPlane: %d, %d", iPixelFormat, iLayerPlane); return FALSE; } // *********************************************************************** int APIENTRY _GLD_WGL_EXPORT(DescribePixelFormat)( HDC a, int b, UINT c, LPPIXELFORMATDESCRIPTOR d) { UINT nSize; // Validate license if (!dglValidate()) return 0; if (d == NULL) // Calling app requires max number of PF's return glb.nPixelFormatCount; // The supplied buffer may be larger than the info that we // will be copying. if (c > sizeof(PIXELFORMATDESCRIPTOR)) nSize = sizeof(PIXELFORMATDESCRIPTOR); else nSize = c; // Setup an empty PFD before doing validation check memset(d, 0, nSize); d->nSize = nSize; d->nVersion = 1; if (!IsValidPFD(b)) return 0; // Bail if PFD index is invalid memcpy(d, &glb.lpPF[b-1].pfd, nSize); return glb.nPixelFormatCount; } // *********************************************************************** HGLRC APIENTRY _GLD_WGL_EXPORT(GetCurrentContext)(void) { // Validate license if (!dglValidate()) return 0; return dglGetCurrentContext(); } // *********************************************************************** HDC APIENTRY _GLD_WGL_EXPORT(GetCurrentDC)(void) { // Validate license if (!dglValidate()) return 0; return dglGetCurrentDC(); } // *********************************************************************** PROC APIENTRY _GLD_WGL_EXPORT(GetDefaultProcAddress)( LPCSTR a) { // Validate license if (!dglValidate()) return NULL; UNSUPPORTED("DGL_GetDefaultProcAddress") return NULL; } // *********************************************************************** int APIENTRY _GLD_WGL_EXPORT(GetLayerPaletteEntries)( HDC a, int b, int c, int d, COLORREF *e) { // Validate license if (!dglValidate()) return 0; UNSUPPORTED("DGL_GetLayerPaletteEntries") return 0; } // *********************************************************************** int APIENTRY _GLD_WGL_EXPORT(GetPixelFormat)( HDC a) { // Validate license if (!dglValidate()) return 0; return dglGetPixelFormat(); } // *********************************************************************** PROC APIENTRY _GLD_WGL_EXPORT(GetProcAddress)( LPCSTR a) { PROC dglGetProcAddressD3D(LPCSTR a); // Validate license if (!dglValidate()) return NULL; #ifdef _USE_GLD3_WGL return _gldDriver.wglGetProcAddress(a); #else return dglGetProcAddressD3D(a); #endif } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(MakeCurrent)( HDC a, HGLRC b) { // Validate license if (!dglValidate()) return FALSE; return dglMakeCurrent(a, b); } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(RealizeLayerPalette)( HDC a, int b, BOOL c) { // Validate license if (!dglValidate()) return FALSE; UNSUPPORTED("DGL_RealizeLayerPalette") return FALSE; } // *********************************************************************** int APIENTRY _GLD_WGL_EXPORT(SetLayerPaletteEntries)( HDC a, int b, int c, int d, CONST COLORREF *e) { // Validate license if (!dglValidate()) return 0; UNSUPPORTED("DGL_SetLayerPaletteEntries") return 0; } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(SetPixelFormat)( HDC a, int b, CONST PIXELFORMATDESCRIPTOR *c) { // Validate license if (!dglValidate()) return FALSE; if (IsValidPFD(b)) { ddlogPrintf(DDLOG_SYSTEM, "SetPixelFormat: PixelFormat %d has been set", b); dglSetPixelFormat(b); return TRUE; } else { ddlogPrintf(DDLOG_ERROR, "SetPixelFormat: PixelFormat %d is invalid and cannot be set", b); return FALSE; } } // *********************************************************************** /* * Share lists between two gl_context structures. * This was added for WIN32 WGL function support, since wglShareLists() * must be called *after* wglCreateContext() with valid GLRCs. (DaveM) */ // // Copied from GLD2.x. KeithH // static GLboolean _gldShareLists( struct gl_context *ctx1, struct gl_context *ctx2) { /* Sanity check context pointers */ if (ctx1 == NULL || ctx2 == NULL) return GL_FALSE; /* Sanity check shared list pointers */ if (ctx1->Shared == NULL || ctx2->Shared == NULL) return GL_FALSE; /* Decrement reference count on sharee to release previous list */ ctx2->Shared->RefCount--; #if 0 /* 3DStudio exits on this memory release */ if (ctx2->Shared->RefCount == 0) free_shared_state(ctx2, ctx2->Shared); #endif /* Re-assign list from sharer to sharee and increment reference count */ ctx2->Shared = ctx1->Shared; ctx1->Shared->RefCount++; return GL_TRUE; } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(ShareLists)( HGLRC a, HGLRC b) { DGL_ctx *dgl1, *dgl2; // Validate license if (!dglValidate()) return FALSE; // Mesa supports shared lists, but you need to supply the shared // GL context info when calling gl_create_context(). An auxiliary // function gl_share_lists() has been added to update the shared // list info after the GL contexts have been created. (DaveM) dgl1 = dglGetContextAddress(a); dgl2 = dglGetContextAddress(b); if (dgl1->bAllocated && dgl2->bAllocated) { #ifdef _USE_GLD3_WGL return _gldShareLists(dgl1->glCtx, dgl2->glCtx); #else return (*mesaFuncs.gl_share_lists)(dgl1->glCtx, dgl2->glCtx); #endif } return FALSE; } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(SwapBuffers)( HDC a) { // Validate license if (!dglValidate()) return FALSE; return dglSwapBuffers(a); } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(SwapLayerBuffers)( HDC a, UINT b) { // Validate license if (!dglValidate()) return FALSE; return dglSwapBuffers(a); } // *********************************************************************** // *********************************************************************** // Note: This ResizeBuffers() function may be called from // either MESA glViewport() or GLD wglMakeCurrent(). BOOL dglWglResizeBuffers( struct gl_context *ctx, BOOL bDefaultDriver) { DGL_ctx *dgl = NULL; RECT rcScreenRect; DWORD dwWidth; DWORD dwHeight; DDSURFACEDESC2 ddsd2; DDSCAPS2 ddscaps2; IDirectDrawClipper *lpddClipper = NULL; DWORD dwFlags; HRESULT hResult; DWORD dwMemoryType; int i; struct gl_texture_object *tObj; struct gl_texture_image *image; BOOL bWasFullscreen; BOOL bSaveDesktop; BOOL bFullScrnWin = FALSE; DDSURFACEDESC2 ddsd2DisplayMode; DDBLTFX ddbltfx; POINT pt; RECT rcDst; #ifdef _USE_GLD3_WGL GLD_displayMode glddm; #endif #define DDLOG_CRITICAL_OR_WARN (bDefaultDriver ? DDLOG_WARN : DDLOG_CRITICAL) // Validate license if (!dglValidate()) return FALSE; // Sanity checks if (ctx == NULL) return FALSE; dgl = ctx->DriverCtx; if (dgl == NULL) return FALSE; // Get the window size and calculate its dimensions if (dgl->hWnd == NULL) { // Check for non-window DC = memory DC ? if (GetClipBox(dgl->hDC, &rcScreenRect) == ERROR) SetRect(&rcScreenRect, 0, 0, 0, 0); } else if (!GetClientRect(dgl->hWnd, &rcScreenRect)) SetRect(&rcScreenRect, 0, 0, 0, 0); dwWidth = rcScreenRect.right - rcScreenRect.left; dwHeight = rcScreenRect.bottom - rcScreenRect.top; CopyRect(&dgl->rcScreenRect, &rcScreenRect); // This will occur on Alt-Tab if ((dwWidth == 0) && (dwHeight == 0)) { //dgl->bCanRender = FALSE; return TRUE; // No resize possible! } // Some apps zero only 1 dimension for non-visible window... (DaveM) if ((dwWidth == 0) || (dwHeight == 0)) { dwWidth = 8; dwHeight = 8; } // Test to see if a resize is required. // Note that the dimensions will be the same if a prior resize attempt failed. if ((dwWidth == dgl->dwWidth) && (dwHeight == dgl->dwHeight) && bDefaultDriver) { return TRUE; // No resize required } ddlogPrintf(DDLOG_SYSTEM, "dglResize: %dx%d", dwWidth, dwHeight); #ifndef _USE_GLD3_WGL // Work out where we want our surfaces created dwMemoryType = (bDefaultDriver) ? glb.dwMemoryType : DDSCAPS_SYSTEMMEMORY; #endif // _USE_GLD3_WGL // Note previous fullscreen vs window display status bWasFullscreen = dgl->bFullscreen; #ifdef _USE_GLD3_WGL if (_gldDriver.GetDisplayMode(dgl, &glddm)) { if ( (dwWidth == glddm.Width) && (dwHeight == glddm.Height) ) { bFullScrnWin = TRUE; } if (bFullScrnWin && glb.bPrimary && !glb.bFullscreenBlit && !glb.bDirectDrawPersistant) { dgl->bFullscreen = TRUE; ddlogMessage(DDLOG_INFO, "Fullscreen window after resize.\n"); } else { dgl->bFullscreen = FALSE; ddlogMessage(DDLOG_INFO, "Non-Fullscreen window after resize.\n"); } // Cache the display mode dimensions dgl->dwModeWidth = glddm.Width; dgl->dwModeHeight = glddm.Height; } // Clamp the effective window dimensions to primary surface. // We need to do this for D3D viewport dimensions even if wide // surfaces are supported. This also is a good idea for handling // whacked-out window dimensions passed for non-drawable windows // like Solid Edge. (DaveM) if (dgl->dwWidth > glddm.Width) dgl->dwWidth = glddm.Width; if (dgl->dwHeight > glddm.Height) dgl->dwHeight = glddm.Height; #else // _USE_GLD3_WGL // Window resize may have changed to fullscreen ZeroMemory(&ddsd2DisplayMode, sizeof(ddsd2DisplayMode)); ddsd2DisplayMode.dwSize = sizeof(ddsd2DisplayMode); hResult = IDirectDraw4_GetDisplayMode( dgl->lpDD4, &ddsd2DisplayMode); if (SUCCEEDED(hResult)) { if ( (dwWidth == ddsd2DisplayMode.dwWidth) && (dwHeight == ddsd2DisplayMode.dwHeight) ) { bFullScrnWin = TRUE; } if (bFullScrnWin && glb.bPrimary && !glb.bFullscreenBlit && !glb.bDirectDrawPersistant) { dgl->bFullscreen = TRUE; ddlogMessage(DDLOG_INFO, "Fullscreen window after resize.\n"); } else { dgl->bFullscreen = FALSE; ddlogMessage(DDLOG_INFO, "Non-Fullscreen window after resize.\n"); } // Cache the display mode dimensions dgl->dwModeWidth = ddsd2DisplayMode.dwWidth; dgl->dwModeHeight = ddsd2DisplayMode.dwHeight; } // Clamp the effective window dimensions to primary surface. // We need to do this for D3D viewport dimensions even if wide // surfaces are supported. This also is a good idea for handling // whacked-out window dimensions passed for non-drawable windows // like Solid Edge. (DaveM) if (dgl->dwWidth > ddsd2DisplayMode.dwWidth) dgl->dwWidth = ddsd2DisplayMode.dwWidth; if (dgl->dwHeight > ddsd2DisplayMode.dwHeight) dgl->dwHeight = ddsd2DisplayMode.dwHeight; #endif // _USE_GLD3_WGL // Note if fullscreen vs window display has changed? bSaveDesktop = (!bWasFullscreen && !dgl->bFullscreen) ? TRUE : FALSE; // Save the desktop primary surface from being destroyed // whenever remaining in windowed mode, since the stereo mode // switches are expensive... #ifndef _USE_GLD3_WGL // Don't need to re-allocate persistant buffers. (DaveM) // Though we should clear the back buffers to hide artifacts. if (glb.bDirectDrawPersistant && glb.bPersistantBuffers) { dgl->dwWidth = dwWidth; dgl->dwHeight = dwHeight; ZeroMemory(&ddbltfx, sizeof(ddbltfx)); ddbltfx.dwSize = sizeof(ddbltfx); ddbltfx.dwFillColor = dgl->dwClearColorPF; IDirectDrawSurface4_Blt(dgl->lpBack4, &rcScreenRect, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &ddbltfx); return TRUE; } // Ensure all rendering is complete if (ctx->Driver.Finish) (*ctx->Driver.Finish)(ctx); if (dgl->bSceneStarted == TRUE) { IDirect3DDevice3_EndScene(dgl->lpDev3); dgl->bSceneStarted = FALSE; } #endif // _USE_GLD3_WGL dgl->bCanRender = FALSE; #ifdef GLD_THREADS // Serialize access to DirectDraw and DDS operations if (glb.bMultiThreaded) EnterCriticalSection(&CriticalSection); #endif #ifndef _USE_GLD3_WGL // Release existing surfaces RELEASE(dgl->lpDev3); RELEASE(dgl->lpDepth4); RELEASE(dgl->lpBack4); if (glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) ; else RELEASE(dgl->lpFront4); #endif // _USE_GLD3_WGL dgl->dwWidth = dwWidth; dgl->dwHeight = dwHeight; // Set defaults dgl->dwModeWidth = dgl->dwWidth; dgl->dwModeHeight = dgl->dwHeight; #ifdef _USE_GLD3_WGL if (!_gldDriver.ResizeDrawable(dgl, bDefaultDriver, glb.bDirectDrawPersistant, glb.bPersistantBuffers)) goto cleanup_and_return_with_error; #else // _USE_GLD3_WGL if (dgl->bFullscreen) { // // FULLSCREEN // // Disable warning popups when in fullscreen mode ddlogWarnOption(FALSE); // Have to release the persistant DirectDraw primary surface // if switching to fullscreen mode. So if application wants // persistant display in fullscreen mode, a fullscreen-size // window should be used instead via fullscreen-blit option. if (glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) { RELEASE(glb.lpPrimary4); glb.bDirectDrawPrimary = FALSE; } dwFlags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT; if (glb.bFastFPU) dwFlags |= DDSCL_FPUSETUP; // optional hResult = IDirectDraw4_SetCooperativeLevel(dgl->lpDD4, dgl->hWnd, dwFlags); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: Unable to set Exclusive Fullscreen mode", hResult); goto cleanup_and_return_with_error; } hResult = IDirectDraw4_SetDisplayMode(dgl->lpDD4, dgl->dwModeWidth, dgl->dwModeHeight, dgl->dwBPP, 0, 0); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: SetDisplayMode failed", hResult); goto cleanup_and_return_with_error; } // ** The display mode has changed, so dont use MessageBox! ** ZeroMemory(&ddsd2, sizeof(ddsd2)); ddsd2.dwSize = sizeof(ddsd2); if (dgl->bDoubleBuffer) { // Double buffered // Primary surface ddsd2.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; ddsd2.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_3DDEVICE | dwMemoryType; ddsd2.dwBackBufferCount = 1; hResult = IDirectDraw4_CreateSurface(dgl->lpDD4, &ddsd2, &dgl->lpFront4, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: CreateSurface (primary) failed", hResult); goto cleanup_and_return_with_error; } // Render target surface ZeroMemory(&ddscaps2, sizeof(ddscaps2)); // Clear the entire struct. ddscaps2.dwCaps = DDSCAPS_BACKBUFFER; hResult = IDirectDrawSurface4_GetAttachedSurface(dgl->lpFront4, &ddscaps2, &dgl->lpBack4); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: GetAttachedSurface failed", hResult); goto cleanup_and_return_with_error; } } else { // Single buffered // Primary surface ddsd2.dwFlags = DDSD_CAPS; ddsd2.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | //DDSCAPS_3DDEVICE | dwMemoryType; hResult = IDirectDraw4_CreateSurface(dgl->lpDD4, &ddsd2, &dgl->lpFront4, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: CreateSurface (primary) failed", hResult); goto cleanup_and_return_with_error; } dgl->lpBack4 = NULL; } } else { // WINDOWED // OK to enable warning popups in windowed mode ddlogWarnOption(glb.bMessageBoxWarnings); // Ditto if persistant DirectDraw primary if (glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) goto DoClipperOnly; // WINDOWED dwFlags = DDSCL_NORMAL; if (glb.bMultiThreaded) dwFlags |= DDSCL_MULTITHREADED; if (glb.bFastFPU) dwFlags |= DDSCL_FPUSETUP; // optional hResult = IDirectDraw4_SetCooperativeLevel(dgl->lpDD4, dgl->hWnd, dwFlags); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: Unable to set Normal coop level", hResult); goto cleanup_and_return_with_error; } // Primary surface ZeroMemory(&ddsd2, sizeof(ddsd2)); ddsd2.dwSize = sizeof(ddsd2); ddsd2.dwFlags = DDSD_CAPS; ddsd2.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; hResult = IDirectDraw4_CreateSurface(dgl->lpDD4, &ddsd2, &dgl->lpFront4, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: CreateSurface (primary) failed", hResult); goto cleanup_and_return_with_error; } // Cache the primary surface for persistant DirectDraw state if (glb.bDirectDrawPersistant && !glb.bDirectDrawPrimary) { glb.lpPrimary4 = dgl->lpFront4; IDirectDrawSurface4_AddRef(glb.lpPrimary4); glb.bDirectDrawPrimary = TRUE; } // Clipper object hResult = DirectDrawCreateClipper(0, &lpddClipper, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: CreateClipper failed", hResult); goto cleanup_and_return_with_error; } hResult = IDirectDrawClipper_SetHWnd(lpddClipper, 0, dgl->hWnd); if (FAILED(hResult)) { RELEASE(lpddClipper); ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: SetHWnd failed", hResult); goto cleanup_and_return_with_error; } hResult = IDirectDrawSurface4_SetClipper(dgl->lpFront4, lpddClipper); RELEASE(lpddClipper); // We have finished with it. if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: SetClipper failed", hResult); goto cleanup_and_return_with_error; } DoClipperOnly: // Update the window for the original clipper if ((glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) || bSaveDesktop) { IDirectDrawSurface4_GetClipper(dgl->lpFront4, &lpddClipper); IDirectDrawClipper_SetHWnd(lpddClipper, 0, dgl->hWnd); RELEASE(lpddClipper); } if (dgl->bDoubleBuffer) { // Render target surface ZeroMemory(&ddsd2, sizeof(ddsd2)); ddsd2.dwSize = sizeof(ddsd2); ddsd2.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd2.dwWidth = dgl->dwWidth; ddsd2.dwHeight = dgl->dwHeight; ddsd2.ddsCaps.dwCaps = DDSCAPS_3DDEVICE | DDSCAPS_OFFSCREENPLAIN | dwMemoryType; hResult = IDirectDraw4_CreateSurface(dgl->lpDD4, &ddsd2, &dgl->lpBack4, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: Create Backbuffer failed", hResult); goto cleanup_and_return_with_error; } } else { dgl->lpBack4 = NULL; } } // // Now create the Zbuffer // if (dgl->bDepthBuffer) { // Get z-buffer dimensions from the render target // Setup the surface desc for the z-buffer. ZeroMemory(&ddsd2, sizeof(ddsd2)); ddsd2.dwSize = sizeof(ddsd2); ddsd2.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; ddsd2.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | dwMemoryType; ddsd2.dwWidth = dgl->dwWidth; ddsd2.dwHeight = dgl->dwHeight; memcpy(&ddsd2.ddpfPixelFormat, &glb.lpZBufferPF[dgl->iZBufferPF], sizeof(DDPIXELFORMAT) ); // Create a z-buffer hResult = IDirectDraw4_CreateSurface(dgl->lpDD4, &ddsd2, &dgl->lpDepth4, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: CreateSurface (ZBuffer) failed", hResult); goto cleanup_and_return_with_error; } // Attach Zbuffer to render target TRY(IDirectDrawSurface4_AddAttachedSurface( dgl->bDoubleBuffer ? dgl->lpBack4 : dgl->lpFront4, dgl->lpDepth4), "dglResize: Attach Zbuffer"); } // Clear the newly resized back buffers for the window client area. ZeroMemory(&ddbltfx, sizeof(ddbltfx)); ddbltfx.dwSize = sizeof(ddbltfx); ddbltfx.dwFillColor = dgl->dwClearColorPF; IDirectDrawSurface4_Blt(dgl->lpBack4, &rcScreenRect, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &ddbltfx); // // Now that we have a zbuffer we can create the 3D device // hResult = IDirect3D3_CreateDevice(dgl->lpD3D3, bDefaultDriver ? &glb.d3dGuid : &IID_IDirect3DRGBDevice, dgl->bDoubleBuffer ? dgl->lpBack4 : dgl->lpFront4, &dgl->lpDev3, NULL); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: Could not create Direct3D device", hResult); goto cleanup_and_return_with_error; } // We must do this as soon as the device is created dglInitStateCaches(dgl); // // Viewport // hResult = IDirect3DDevice3_AddViewport(dgl->lpDev3, dgl->lpViewport3); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: AddViewport failed", hResult); goto cleanup_and_return_with_error; } // Initialise the viewport dgl->d3dViewport.dwSize = sizeof(dgl->d3dViewport); dgl->d3dViewport.dwX = 0; dgl->d3dViewport.dwY = 0; dgl->d3dViewport.dwWidth = dgl->dwWidth; dgl->d3dViewport.dwHeight = dgl->dwHeight; dgl->d3dViewport.dvClipX = 0; dgl->d3dViewport.dvClipY = 0; dgl->d3dViewport.dvClipWidth = dgl->dwWidth; dgl->d3dViewport.dvClipHeight = dgl->dwHeight; // dgl->d3dViewport.dvMinZ = 0.0f; // dgl->d3dViewport.dvMaxZ = 1.0f; TRY(IDirect3DViewport3_SetViewport2(dgl->lpViewport3, &dgl->d3dViewport), "dglResize: SetViewport2"); hResult = IDirect3DDevice3_SetCurrentViewport(dgl->lpDev3, dgl->lpViewport3); if (FAILED(hResult)) { ddlogError(DDLOG_CRITICAL_OR_WARN, "dglResize: SetCurrentViewport failed", hResult); goto cleanup_and_return_with_error; } // (Re)Initialise all the Direct3D renderstates dglInitStateD3D(ctx); // Now we have to recreate all of our textures (+ mipmaps). // Walk over all textures in hash table // XXX what about the default texture objects (id=0)? { struct _mesa_HashTable *textures = ctx->Shared->TexObjects; GLuint id; for (id = _mesa_HashFirstEntry(textures); id; id = _mesa_HashNextEntry(textures, id)) { tObj = (struct gl_texture_object *) _mesa_HashLookup(textures, id); if (tObj->DriverData) { // We could call our TexImage function directly, but it's // safer to use the driver pointer. for (i=0; iImage[i]; if (image) { switch (tObj->Dimensions){ case 1: if (ctx->Driver.TexImage) (*ctx->Driver.TexImage)(ctx, GL_TEXTURE_1D, tObj, i, image->Format, image); break; case 2: if (ctx->Driver.TexImage) (*ctx->Driver.TexImage)(ctx, GL_TEXTURE_2D, tObj, i, image->Format, image); break; default: break; } } } } } } // Re-Bind each texture Unit for (i=0; iTexture.Unit[i].Current; if (tObj) { DGL_texture *lpTex = (DGL_texture *)tObj->DriverData; hResult = dglSetTexture(dgl, i, lpTex ? lpTex->lpTexture : NULL); if (FAILED(hResult)) { ddlogError(DDLOG_ERROR, "dglResize: SetTexture failed", hResult); } } } #endif // _USE_GLD3_WGL dgl->bCanRender = TRUE; #ifdef GLD_THREADS // Release serialized access if (glb.bMultiThreaded) LeaveCriticalSection(&CriticalSection); #endif // SUCCESS. return TRUE; cleanup_and_return_with_error: // Relase all interfaces before returning. #ifdef _USE_GLD3_WGL _gldDriver.DestroyDrawable(dgl); #else // _USE_GLD3_WGL RELEASE(dgl->lpDev3); RELEASE(dgl->lpDepth4); RELEASE(dgl->lpBack4); if (glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) ; else RELEASE(dgl->lpFront4); #undef DDLOG_CRITICAL_OR_WARN #endif // _USE_GLD3_WGL // Mark context as not being able to render dgl->bCanRender = FALSE; #ifdef GLD_THREADS // Release serialized access if (glb.bMultiThreaded) LeaveCriticalSection(&CriticalSection); #endif return FALSE; } // *********************************************************************** // *********************************************************************** // Support for bitmap fonts. // *********************************************************************** // *********************************************************************** /***************************************************************************** ** ** InvertGlyphBitmap. ** ** Invert the bitmap so that it suits OpenGL's representation. ** Each row starts on a double word boundary. ** *****************************************************************************/ static void InvertGlyphBitmap( int w, int h, DWORD *fptr, DWORD *tptr) { int dWordsInRow = (w+31)/32; int i, j; DWORD *tmp = tptr; if (w <= 0 || h <= 0) { return; } tptr += ((h-1)*dWordsInRow); for (i = 0; i < h; i++) { for (j = 0; j < dWordsInRow; j++) { *(tptr + j) = *(fptr + j); } tptr -= dWordsInRow; fptr += dWordsInRow; } } // *********************************************************************** /***************************************************************************** * wglUseFontBitmaps * * Converts a subrange of the glyphs in a GDI font to OpenGL display * lists. * * Extended to support any GDI font, not just TrueType fonts. (DaveM) * *****************************************************************************/ BOOL APIENTRY _GLD_WGL_EXPORT(UseFontBitmapsA)( HDC hDC, DWORD first, DWORD count, DWORD listBase) { int i, ox, oy, ix, iy; int w, h; int iBufSize, iCurBufSize = 0; DWORD *bitmapBuffer = NULL; DWORD *invertedBitmapBuffer = NULL; BOOL bSuccessOrFail = TRUE; BOOL bTrueType = FALSE; TEXTMETRIC tm; GLYPHMETRICS gm; RASTERIZER_STATUS rs; MAT2 mat; SIZE size; RECT rect; HDC hDCMem; HBITMAP hBitmap; BITMAPINFO bmi; HFONT hFont; // Validate SciTech DirectGL license if (!dglValidate()) return FALSE; // Set up a unity matrix. ZeroMemory(&mat, sizeof(mat)); mat.eM11.value = 1; mat.eM22.value = 1; // Test to see if selected font is TrueType or not ZeroMemory(&tm, sizeof(tm)); if (!GetTextMetrics(hDC, &tm)) { ddlogMessage(DDLOG_ERROR, "DGL_UseFontBitmaps: Font metrics error\n"); return (FALSE); } bTrueType = (tm.tmPitchAndFamily & TMPF_TRUETYPE) ? TRUE : FALSE; // Test to see if TRUE-TYPE capabilities are installed // (only necessary if TrueType font selected) ZeroMemory(&rs, sizeof(rs)); if (bTrueType) { if (!GetRasterizerCaps (&rs, sizeof (RASTERIZER_STATUS))) { ddlogMessage(DDLOG_ERROR, "DGL_UseFontBitmaps: Raster caps error\n"); return (FALSE); } if (!(rs.wFlags & TT_ENABLED)) { ddlogMessage(DDLOG_ERROR, "DGL_UseFontBitmaps: No TrueType caps\n"); return (FALSE); } } // Trick to get the current font handle hFont = SelectObject(hDC, GetStockObject(SYSTEM_FONT)); SelectObject(hDC, hFont); // Have memory device context available for holding bitmaps of font glyphs hDCMem = CreateCompatibleDC(hDC); SelectObject(hDCMem, hFont); SetTextColor(hDCMem, RGB(0xFF, 0xFF, 0xFF)); SetBkColor(hDCMem, 0); for (i = first; (DWORD) i < (first + count); i++) { // Find out how much space is needed for the bitmap so we can // Set the buffer size correctly. if (bTrueType) { // Use TrueType support to get bitmap size of glyph iBufSize = GetGlyphOutline(hDC, i, GGO_BITMAP, &gm, 0, NULL, &mat); if (iBufSize == GDI_ERROR) { bSuccessOrFail = FALSE; break; } } else { // Use generic GDI support to compute bitmap size of glyph w = tm.tmMaxCharWidth; h = tm.tmHeight; if (GetTextExtentPoint32(hDC, (LPCTSTR)&i, 1, &size)) { w = size.cx; h = size.cy; } iBufSize = w * h; // Use DWORD multiple for compatibility iBufSize += 3; iBufSize /= 4; iBufSize *= 4; } // If we need to allocate Larger Buffers, then do so - but allocate // An extra 50 % so that we don't do too many mallocs ! if (iBufSize > iCurBufSize) { if (bitmapBuffer) { __wglFree(bitmapBuffer); } if (invertedBitmapBuffer) { __wglFree(invertedBitmapBuffer); } iCurBufSize = iBufSize * 2; bitmapBuffer = (DWORD *) __wglMalloc(iCurBufSize); invertedBitmapBuffer = (DWORD *) __wglMalloc(iCurBufSize); if (bitmapBuffer == NULL || invertedBitmapBuffer == NULL) { bSuccessOrFail = FALSE; break; } } // If we fail to get the Glyph data, delete the display lists // Created so far and return FALSE. if (bTrueType) { // Use TrueType support to get bitmap of glyph if (GetGlyphOutline(hDC, i, GGO_BITMAP, &gm, iBufSize, bitmapBuffer, &mat) == GDI_ERROR) { bSuccessOrFail = FALSE; break; } // Setup glBitmap parameters for current font glyph w = gm.gmBlackBoxX; h = gm.gmBlackBoxY; ox = gm.gmptGlyphOrigin.x; oy = gm.gmptGlyphOrigin.y; ix = gm.gmCellIncX; iy = gm.gmCellIncY; } else { // Use generic GDI support to create bitmap of glyph ZeroMemory(bitmapBuffer, iBufSize); if (i >= tm.tmFirstChar && i <= tm.tmLastChar) { // Only create bitmaps for actual font glyphs hBitmap = CreateBitmap(w, h, 1, 1, NULL); SelectObject(hDCMem, hBitmap); // Make bitmap of current font glyph SetRect(&rect, 0, 0, w, h); DrawText(hDCMem, (LPCTSTR)&i, 1, &rect, DT_LEFT | DT_BOTTOM | DT_SINGLELINE | DT_NOCLIP); // Make copy of bitmap in our local buffer ZeroMemory(&bmi, sizeof(bmi)); bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = -h; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 1; bmi.bmiHeader.biCompression = BI_RGB; GetDIBits(hDCMem, hBitmap, 0, h, bitmapBuffer, &bmi, 0); DeleteObject(hBitmap); } else { // Otherwise use empty display list for non-existing glyph iBufSize = 0; } // Setup glBitmap parameters for current font glyph ox = 0; oy = tm.tmDescent; ix = w; iy = 0; } // Create an OpenGL display list. _GLD_glNewList((listBase + i), GL_COMPILE); // Some fonts have no data for the space character, yet advertise // a non-zero size. if (0 == iBufSize) { _GLD_glBitmap(0, 0, 0.0f, 0.0f, (GLfloat) ix, (GLfloat) iy, NULL); } else { // Invert the Glyph data. InvertGlyphBitmap(w, h, bitmapBuffer, invertedBitmapBuffer); // Render an OpenGL bitmap and invert the origin. _GLD_glBitmap(w, h, (GLfloat) ox, (GLfloat) (h-oy), (GLfloat) ix, (GLfloat) iy, (GLubyte *) invertedBitmapBuffer); } // Close this display list. _GLD_glEndList(); } if (bSuccessOrFail == FALSE) { ddlogMessage(DDLOG_ERROR, "DGL_UseFontBitmaps: Get glyph failed\n"); _GLD_glDeleteLists((i+listBase), (i-first)); } // Release resources used DeleteObject(hFont); DeleteDC(hDCMem); if (bitmapBuffer) __wglFree(bitmapBuffer); if (invertedBitmapBuffer) __wglFree(invertedBitmapBuffer); return(bSuccessOrFail); } // *********************************************************************** BOOL APIENTRY _GLD_WGL_EXPORT(UseFontBitmapsW)( HDC a, DWORD b, DWORD c, DWORD d) { // Validate license if (!dglValidate()) return FALSE; return _GLD_WGL_EXPORT(UseFontBitmapsA)(a, b, c, d); } // *********************************************************************** // *********************************************************************** // Support for outline TrueType fonts. // *********************************************************************** // *********************************************************************** void * __wglRealloc( void *oldPtr, size_t newSize) { void *newPtr = NULL; if (newSize != 0) { newPtr = (void *) GlobalAlloc(GPTR, newSize); if (oldPtr && newPtr) { DWORD oldSize = GlobalSize(oldPtr); memcpy(newPtr, oldPtr, (oldSize <= newSize ? oldSize : newSize)); GlobalFree(oldPtr); } } else if (oldPtr) { GlobalFree(oldPtr); } if (newPtr == NULL) { return NULL; /* XXX out of memory error */ } return newPtr; } // *********************************************************************** /***************************************************************************** * wglUseFontOutlinesW * * Converts a subrange of the glyphs in a TrueType font to OpenGL display * lists. *****************************************************************************/ BOOL APIENTRY _GLD_WGL_EXPORT(UseFontOutlinesW)( IN HDC hDC, IN DWORD first, IN DWORD count, IN DWORD listBase, IN FLOAT chordalDeviation, IN FLOAT extrusion, IN INT format, OUT LPGLYPHMETRICSFLOAT lpgmf) { return _GLD_WGL_EXPORT(UseFontOutlinesA)(hDC, first, count, listBase, chordalDeviation, extrusion, format, lpgmf); } /***************************************************************************** * wglUseFontOutlinesA * * Converts a subrange of the glyphs in a TrueType font to OpenGL display * lists. *****************************************************************************/ BOOL APIENTRY _GLD_WGL_EXPORT(UseFontOutlinesA)( IN HDC hDC, IN DWORD first, IN DWORD count, IN DWORD listBase, IN FLOAT chordalDeviation, IN FLOAT extrusion, IN INT format, OUT LPGLYPHMETRICSFLOAT glyphMetricsFloatArray) { DWORD glyphIndex; UCHAR* glyphBuf; DWORD glyphBufSize; /* * Flush any previous OpenGL errors. This allows us to check for * new errors so they can be reported via the function return value. */ while (_GLD_glGetError() != GL_NO_ERROR) ; /* * Make sure that the current font can be sampled accurately. */ hNewFont = CreateHighResolutionFont(hDC); if (!hNewFont) return FALSE; hOldFont = SelectObject(hDC, hNewFont); if (!hOldFont) return FALSE; /* * Preallocate a buffer for the outline data, and track its size: */ glyphBuf = (UCHAR*) __wglMalloc(glyphBufSize = 10240); if (!glyphBuf) return FALSE; /*WGL_STATUS_NOT_ENOUGH_MEMORY*/ /* * Process each glyph in the given range: */ for (glyphIndex = first; glyphIndex - first < count; ++glyphIndex) { GLYPHMETRICS glyphMetrics; DWORD glyphSize; static MAT2 matrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} }; LPGLYPHMETRICSFLOAT glyphMetricsFloat = &glyphMetricsFloatArray[glyphIndex - first]; /* * Determine how much space is needed to store the glyph's * outlines. If our glyph buffer isn't large enough, * resize it. */ glyphSize = GetGlyphOutline( hDC, glyphIndex, GGO_NATIVE, &glyphMetrics, 0, NULL, &matrix ); if (glyphSize < 0) return FALSE; /*WGL_STATUS_FAILURE*/ if (glyphSize > glyphBufSize) { __wglFree(glyphBuf); glyphBuf = (UCHAR*) __wglMalloc(glyphBufSize = glyphSize); if (!glyphBuf) return FALSE; /*WGL_STATUS_NOT_ENOUGH_MEMORY*/ } /* * Get the glyph's outlines. */ if (GetGlyphOutline( hDC, glyphIndex, GGO_NATIVE, &glyphMetrics, glyphBufSize, glyphBuf, &matrix ) < 0) { __wglFree(glyphBuf); return FALSE; /*WGL_STATUS_FAILURE*/ } glyphMetricsFloat->gmfBlackBoxX = (FLOAT) glyphMetrics.gmBlackBoxX * ScaleFactor; glyphMetricsFloat->gmfBlackBoxY = (FLOAT) glyphMetrics.gmBlackBoxY * ScaleFactor; glyphMetricsFloat->gmfptGlyphOrigin.x = (FLOAT) glyphMetrics.gmptGlyphOrigin.x * ScaleFactor; glyphMetricsFloat->gmfptGlyphOrigin.y = (FLOAT) glyphMetrics.gmptGlyphOrigin.y * ScaleFactor; glyphMetricsFloat->gmfCellIncX = (FLOAT) glyphMetrics.gmCellIncX * ScaleFactor; glyphMetricsFloat->gmfCellIncY = (FLOAT) glyphMetrics.gmCellIncY * ScaleFactor; /* * Turn the glyph into a display list: */ if (!MakeDisplayListFromGlyph( (glyphIndex - first) + listBase, glyphBuf, glyphSize, glyphMetricsFloat, chordalDeviation + ScaleFactor, extrusion, format)) { __wglFree(glyphBuf); return FALSE; /*WGL_STATUS_FAILURE*/ } } /* * Clean up temporary storage and return. If an error occurred, * clear all OpenGL error flags and return FAILURE status; * otherwise just return SUCCESS. */ __wglFree(glyphBuf); SelectObject(hDC, hOldFont); if (_GLD_glGetError() == GL_NO_ERROR) return TRUE; /*WGL_STATUS_SUCCESS*/ else { while (_GLD_glGetError() != GL_NO_ERROR) ; return FALSE; /*WGL_STATUS_FAILURE*/ } } /***************************************************************************** * CreateHighResolutionFont * * Gets metrics for the current font and creates an equivalent font * scaled to the design units of the font. * *****************************************************************************/ static HFONT CreateHighResolutionFont(HDC hDC) { UINT otmSize; OUTLINETEXTMETRIC *otm; LONG fontHeight, fontWidth, fontUnits; LOGFONT logFont; otmSize = GetOutlineTextMetrics(hDC, 0, NULL); if (otmSize == 0) return NULL; otm = (OUTLINETEXTMETRIC *) __wglMalloc(otmSize); if (otm == NULL) return NULL; otm->otmSize = otmSize; if (GetOutlineTextMetrics(hDC, otmSize, otm) == 0) return NULL; fontHeight = otm->otmTextMetrics.tmHeight - otm->otmTextMetrics.tmInternalLeading; fontWidth = otm->otmTextMetrics.tmAveCharWidth; fontUnits = (LONG) otm->otmEMSquare; ScaleFactor = 1.0F / (FLOAT) fontUnits; logFont.lfHeight = - ((LONG) fontUnits); logFont.lfWidth = (LONG) ((FLOAT) (fontWidth * fontUnits) / (FLOAT) fontHeight); logFont.lfEscapement = 0; logFont.lfOrientation = 0; logFont.lfWeight = otm->otmTextMetrics.tmWeight; logFont.lfItalic = otm->otmTextMetrics.tmItalic; logFont.lfUnderline = otm->otmTextMetrics.tmUnderlined; logFont.lfStrikeOut = otm->otmTextMetrics.tmStruckOut; logFont.lfCharSet = otm->otmTextMetrics.tmCharSet; logFont.lfOutPrecision = OUT_OUTLINE_PRECIS; logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; logFont.lfQuality = DEFAULT_QUALITY; logFont.lfPitchAndFamily = otm->otmTextMetrics.tmPitchAndFamily & 0xf0; strcpy(logFont.lfFaceName, (char *)otm + (int)otm->otmpFaceName); hNewFont = CreateFontIndirect(&logFont); if (hNewFont == NULL) return NULL; __wglFree(otm); return hNewFont; } /***************************************************************************** * MakeDisplayListFromGlyph * * Converts the outline of a glyph to an OpenGL display list. * * Return value is nonzero for success, zero for failure. * * Does not check for OpenGL errors, so if the caller needs to know about them, * it should call glGetError(). *****************************************************************************/ static int MakeDisplayListFromGlyph( IN DWORD listName, IN UCHAR* glyphBuf, IN DWORD glyphSize, IN LPGLYPHMETRICSFLOAT glyphMetricsFloat, IN FLOAT chordalDeviation, IN FLOAT extrusion, IN INT format) { int status; _GLD_glNewList(listName, GL_COMPILE); status = DrawGlyph( glyphBuf, glyphSize, chordalDeviation, extrusion, format); _GLD_glTranslatef(glyphMetricsFloat->gmfCellIncX, glyphMetricsFloat->gmfCellIncY, 0.0F); _GLD_glEndList(); return status; } /***************************************************************************** * DrawGlyph * * Converts the outline of a glyph to OpenGL drawing primitives, tessellating * as needed, and then draws the glyph. Tessellation of the quadratic splines * in the outline is controlled by "chordalDeviation", and the drawing * primitives (lines or polygons) are selected by "format". * * Return value is nonzero for success, zero for failure. * * Does not check for OpenGL errors, so if the caller needs to know about them, * it should call glGetError(). *****************************************************************************/ static int DrawGlyph( IN UCHAR* glyphBuf, IN DWORD glyphSize, IN FLOAT chordalDeviation, IN FLOAT extrusion, IN INT format) { INT status = 0; FLOAT* p; DWORD loop; DWORD point; GLUtesselator* tess = NULL; /* * Initialize the global buffer into which we place the outlines: */ if (!InitLineBuf()) goto exit; /* * Convert the glyph outlines to a set of polyline loops. * (See MakeLinesFromGlyph() for the format of the loop data * structure.) */ if (!MakeLinesFromGlyph(glyphBuf, glyphSize, chordalDeviation)) goto exit; p = LineBuf; /* * Now draw the loops in the appropriate format: */ if (format == WGL_FONT_LINES) { /* * This is the easy case. Just draw the outlines. */ for (loop = (DWORD) *p++; loop; --loop) { _GLD_glBegin(GL_LINE_LOOP); for (point = (DWORD) *p++; point; --point) { _GLD_glVertex2fv(p); p += 2; } _GLD_glEnd(); } status = 1; } else if (format == WGL_FONT_POLYGONS) { double v[3]; FLOAT *save_p = p; GLfloat z_value; /* * This is the hard case. We have to set up a tessellator * to convert the outlines into a set of polygonal * primitives, which the tessellator passes to some * auxiliary routines for drawing. */ if (!LoadGLUTesselator()) goto exit; if (!InitVertBuf()) goto exit; if (!(tess = gluNewTessProc())) goto exit; gluTessCallbackProc(tess, GLU_BEGIN, (void(CALLBACK *)()) _GLD_glBegin); gluTessCallbackProc(tess, GLU_TESS_VERTEX_DATA, (void(CALLBACK *)()) TessVertexOutData); gluTessCallbackProc(tess, GLU_END, (void(CALLBACK *)()) _GLD_glEnd); gluTessCallbackProc(tess, GLU_ERROR, (void(CALLBACK *)()) TessError); gluTessCallbackProc(tess, GLU_TESS_COMBINE, (void(CALLBACK *)()) TessCombine); gluTessNormalProc(tess, 0.0F, 0.0F, 1.0F); TessErrorOccurred = 0; _GLD_glNormal3f(0.0f, 0.0f, 1.0f); v[2] = 0.0; z_value = 0.0f; gluTessBeginPolygonProc(tess, (void *)*(int *)&z_value); for (loop = (DWORD) *p++; loop; --loop) { gluTessBeginContourProc(tess); for (point = (DWORD) *p++; point; --point) { v[0] = p[0]; v[1] = p[1]; gluTessVertexProc(tess, v, p); p += 2; } gluTessEndContourProc(tess); } gluTessEndPolygonProc(tess); status = !TessErrorOccurred; /* Extrusion code */ if (extrusion) { DWORD loops; GLfloat thickness = (GLfloat) -extrusion; FLOAT *vert, *vert2; DWORD count; p = save_p; loops = (DWORD) *p++; for (loop = 0; loop < loops; loop++) { GLfloat dx, dy, len; DWORD last; count = (DWORD) *p++; _GLD_glBegin(GL_QUAD_STRIP); /* Check if the first and last vertex are identical * so we don't draw the same quad twice. */ vert = p + (count-1)*2; last = (p[0] == vert[0] && p[1] == vert[1]) ? count-1 : count; for (point = 0; point <= last; point++) { vert = p + 2 * (point % last); vert2 = p + 2 * ((point+1) % last); dx = vert[0] - vert2[0]; dy = vert[1] - vert2[1]; len = (GLfloat)sqrt(dx * dx + dy * dy); _GLD_glNormal3f(dy / len, -dx / len, 0.0f); _GLD_glVertex3f((GLfloat) vert[0], (GLfloat) vert[1], thickness); _GLD_glVertex3f((GLfloat) vert[0], (GLfloat) vert[1], 0.0f); } _GLD_glEnd(); p += count*2; } /* Draw the back face */ p = save_p; v[2] = thickness; _GLD_glNormal3f(0.0f, 0.0f, -1.0f); gluTessNormalProc(tess, 0.0F, 0.0F, -1.0F); gluTessBeginPolygonProc(tess, (void *)*(int *)&thickness); for (loop = (DWORD) *p++; loop; --loop) { count = (DWORD) *p++; gluTessBeginContourProc(tess); for (point = 0; point < count; point++) { vert = p + ((count-point-1)<<1); v[0] = vert[0]; v[1] = vert[1]; gluTessVertexProc(tess, v, vert); } p += count*2; gluTessEndContourProc(tess); } gluTessEndPolygonProc(tess); } #if DEBUG if (TessErrorOccurred) printf("Tessellation error %s\n", gluErrorString(TessErrorOccurred)); #endif } exit: FreeLineBuf(); if (tess) gluDeleteTessProc(tess); // UnloadGLUTesselator(); FreeVertBuf(); return status; } /***************************************************************************** * LoadGLUTesselator * * Maps the glu32.dll module and gets function pointers for the * tesselator functions. *****************************************************************************/ static BOOL LoadGLUTesselator(void) { if (gluModuleHandle != NULL) return TRUE; { extern HINSTANCE hInstanceOpenGL; char *gluName = "GLU32.DLL"; // char name[256]; // char *ptr; // int len; /* len = GetModuleFileName(hInstanceOpenGL, name, 255); if (len != 0) { ptr = name+len-1; while (ptr > name && *ptr != '\\') ptr--; if (*ptr == '\\') ptr++; if (!stricmp(ptr, "cosmogl.dll")) { gluName = "COSMOGLU.DLL"; } else if (!stricmp(ptr, "opengl32.dll")) { gluName = "GLU32.DLL"; } } */ if ((gluModuleHandle = LoadLibrary(gluName)) == NULL) return FALSE; } if ((gluNewTessProc = (gluNewTessProto) GetProcAddress(gluModuleHandle, "gluNewTess")) == NULL) return FALSE; if ((gluDeleteTessProc = (gluDeleteTessProto) GetProcAddress(gluModuleHandle, "gluDeleteTess")) == NULL) return FALSE; if ((gluTessBeginPolygonProc = (gluTessBeginPolygonProto) GetProcAddress(gluModuleHandle, "gluTessBeginPolygon")) == NULL) return FALSE; if ((gluTessBeginContourProc = (gluTessBeginContourProto) GetProcAddress(gluModuleHandle, "gluTessBeginContour")) == NULL) return FALSE; if ((gluTessVertexProc = (gluTessVertexProto) GetProcAddress(gluModuleHandle, "gluTessVertex")) == NULL) return FALSE; if ((gluTessEndContourProc = (gluTessEndContourProto) GetProcAddress(gluModuleHandle, "gluTessEndContour")) == NULL) return FALSE; if ((gluTessEndPolygonProc = (gluTessEndPolygonProto) GetProcAddress(gluModuleHandle, "gluTessEndPolygon")) == NULL) return FALSE; if ((gluTessPropertyProc = (gluTessPropertyProto) GetProcAddress(gluModuleHandle, "gluTessProperty")) == NULL) return FALSE; if ((gluTessNormalProc = (gluTessNormalProto) GetProcAddress(gluModuleHandle, "gluTessNormal")) == NULL) return FALSE; if ((gluTessCallbackProc = (gluTessCallbackProto) GetProcAddress(gluModuleHandle, "gluTessCallback")) == NULL) return FALSE; return TRUE; } /***************************************************************************** * UnloadGLUTesselator * * Unmaps the glu32.dll module. *****************************************************************************/ static BOOL UnloadGLUTesselator(void) { if (gluModuleHandle != NULL) if (FreeLibrary(gluModuleHandle) == FALSE) return FALSE; gluModuleHandle = NULL; } /***************************************************************************** * TessVertexOut * * Used by tessellator to handle output vertexes. *****************************************************************************/ static void CALLBACK TessVertexOut(FLOAT p[3]) { GLfloat v[2]; v[0] = p[0] * ScaleFactor; v[1] = p[1] * ScaleFactor; _GLD_glVertex2fv(v); } static void CALLBACK TessVertexOutData(FLOAT p[3], GLfloat z) { GLfloat v[3]; v[0] = (GLfloat) p[0]; v[1] = (GLfloat) p[1]; v[2] = z; _GLD_glVertex3fv(v); } /***************************************************************************** * TessCombine * * Used by tessellator to handle self-intersecting contours and degenerate * geometry. *****************************************************************************/ static void CALLBACK TessCombine(double coords[3], void* vertex_data[4], FLOAT weight[4], void** outData) { if (!AppendToVertBuf((FLOAT) coords[0]) || !AppendToVertBuf((FLOAT) coords[1]) || !AppendToVertBuf((FLOAT) coords[2])) TessErrorOccurred = GL_OUT_OF_MEMORY; *outData = VertBuf + (VertBufIndex - 3); } /***************************************************************************** * TessError * * Saves the last tessellator error code in the global TessErrorOccurred. *****************************************************************************/ static void CALLBACK TessError(GLenum error) { TessErrorOccurred = error; } /***************************************************************************** * MakeLinesFromGlyph * * Converts the outline of a glyph from the TTPOLYGON format to a simple * array of floating-point values containing one or more loops. * * The first element of the output array is a count of the number of loops. * The loop data follows this count. Each loop consists of a count of the * number of vertices it contains, followed by the vertices. Each vertex * is an X and Y coordinate. For example, a single triangle might be * described by this array: * * 1., 3., 0., 0., 1., 0., 0., 1. * ^ ^ ^ ^ ^ ^ ^ ^ * #loops #verts x1 y1 x2 y2 x3 y3 * * A two-loop glyph would look like this: * * 2., 3., 0.,0., 1.,0., 0.,1., 3., .2,.2, .4,.2, .2,.4 * * Line segments from the TTPOLYGON are transferred to the output array in * the obvious way. Quadratic splines in the TTPOLYGON are converted to * collections of line segments *****************************************************************************/ static int MakeLinesFromGlyph(IN UCHAR* glyphBuf, IN DWORD glyphSize, IN FLOAT chordalDeviation) { UCHAR* p; int status = 0; /* * Pick up all the polygons (aka loops) that make up the glyph: */ if (!AppendToLineBuf(0.0F)) /* loop count at LineBuf[0] */ goto exit; p = glyphBuf; while (p < glyphBuf + glyphSize) { if (!MakeLinesFromTTPolygon(&p, chordalDeviation)) goto exit; LineBuf[0] += 1.0F; /* increment loop count */ } status = 1; exit: return status; } /***************************************************************************** * MakeLinesFromTTPolygon * * Converts a TTPOLYGONHEADER and its associated curve structures into a * single polyline loop in the global LineBuf. *****************************************************************************/ static int MakeLinesFromTTPolygon( IN OUT UCHAR** pp, IN FLOAT chordalDeviation) { DWORD polySize; UCHAR* polyStart; DWORD vertexCountIndex; /* * Record where the polygon data begins, and where the loop's * vertex count resides: */ polyStart = *pp; vertexCountIndex = LineBufIndex; if (!AppendToLineBuf(0.0F)) return 0; /* * Extract relevant data from the TTPOLYGONHEADER: */ polySize = GetDWord(pp); if (GetDWord(pp) != TT_POLYGON_TYPE) /* polygon type */ return 0; if (!AppendToLineBuf((FLOAT) GetFixed(pp))) /* first X coord */ return 0; if (!AppendToLineBuf((FLOAT) GetFixed(pp))) /* first Y coord */ return 0; LineBuf[vertexCountIndex] += 1.0F; /* * Process each of the TTPOLYCURVE structures in the polygon: */ while (*pp < polyStart + polySize) if (!MakeLinesFromTTPolycurve( pp, vertexCountIndex, chordalDeviation)) return 0; return 1; } /***************************************************************************** * MakeLinesFromTTPolyCurve * * Converts the lines and splines in a single TTPOLYCURVE structure to points * in the global LineBuf. *****************************************************************************/ static int MakeLinesFromTTPolycurve( IN OUT UCHAR** pp, IN DWORD vertexCountIndex, IN FLOAT chordalDeviation) { WORD type; WORD pointCount; /* * Pick up the relevant fields of the TTPOLYCURVE structure: */ type = (WORD) GetWord(pp); pointCount = (WORD) GetWord(pp); /* * Convert the "curve" to line segments: */ if (type == TT_PRIM_LINE) return MakeLinesFromTTLine( pp, vertexCountIndex, pointCount); else if (type == TT_PRIM_QSPLINE) return MakeLinesFromTTQSpline( pp, vertexCountIndex, pointCount, chordalDeviation); else return 0; } /***************************************************************************** * MakeLinesFromTTLine * * Converts points from the polyline in a TT_PRIM_LINE structure to * equivalent points in the global LineBuf. *****************************************************************************/ static int MakeLinesFromTTLine( IN OUT UCHAR** pp, IN DWORD vertexCountIndex, IN WORD pointCount) { /* * Just copy the line segments into the line buffer (converting * type as we go): */ LineBuf[vertexCountIndex] += pointCount; while (pointCount--) { if (!AppendToLineBuf((FLOAT) GetFixed(pp)) /* X coord */ || !AppendToLineBuf((FLOAT) GetFixed(pp))) /* Y coord */ return 0; } return 1; } /***************************************************************************** * MakeLinesFromTTQSpline * * Converts points from the poly quadratic spline in a TT_PRIM_QSPLINE * structure to polyline points in the global LineBuf. *****************************************************************************/ static int MakeLinesFromTTQSpline( IN OUT UCHAR** pp, IN DWORD vertexCountIndex, IN WORD pointCount, IN FLOAT chordalDeviation) { FLOAT x0, y0, x1, y1, x2, y2; WORD point; /* * Process each of the non-interpolated points in the outline. * To do this, we need to generate two interpolated points (the * start and end of the arc) for each non-interpolated point. * The first interpolated point is always the one most recently * stored in LineBuf, so we just extract it from there. The * second interpolated point is either the average of the next * two points in the QSpline, or the last point in the QSpline * if only one remains. */ for (point = 0; point < pointCount - 1; ++point) { x0 = LineBuf[LineBufIndex - 2]; y0 = LineBuf[LineBufIndex - 1]; x1 = (FLOAT) GetFixed(pp); y1 = (FLOAT) GetFixed(pp); if (point == pointCount - 2) { /* * This is the last arc in the QSpline. The final * point is the end of the arc. */ x2 = (FLOAT) GetFixed(pp); y2 = (FLOAT) GetFixed(pp); } else { /* * Peek at the next point in the input to compute * the end of the arc: */ x2 = 0.5F * (x1 + (FLOAT) GetFixed(pp)); y2 = 0.5F * (y1 + (FLOAT) GetFixed(pp)); /* * Push the point back onto the input so it will * be reused as the next off-curve point: */ *pp -= 8; } if (!MakeLinesFromArc( x0, y0, x1, y1, x2, y2, vertexCountIndex, chordalDeviation * chordalDeviation)) return 0; } return 1; } /***************************************************************************** * MakeLinesFromArc * * Subdivides one arc of a quadratic spline until the chordal deviation * tolerance requirement is met, then places the resulting set of line * segments in the global LineBuf. *****************************************************************************/ static int MakeLinesFromArc( IN FLOAT x0, IN FLOAT y0, IN FLOAT x1, IN FLOAT y1, IN FLOAT x2, IN FLOAT y2, IN DWORD vertexCountIndex, IN FLOAT chordalDeviationSquared) { FLOAT x01; FLOAT y01; FLOAT x12; FLOAT y12; FLOAT midPointX; FLOAT midPointY; FLOAT deltaX; FLOAT deltaY; /* * Calculate midpoint of the curve by de Casteljau: */ x01 = 0.5F * (x0 + x1); y01 = 0.5F * (y0 + y1); x12 = 0.5F * (x1 + x2); y12 = 0.5F * (y1 + y2); midPointX = 0.5F * (x01 + x12); midPointY = 0.5F * (y01 + y12); /* * Estimate chordal deviation by the distance from the midpoint * of the curve to its non-interpolated control point. If this * distance is greater than the specified chordal deviation * constraint, then subdivide. Otherwise, generate polylines * from the three control points. */ deltaX = midPointX - x1; deltaY = midPointY - y1; if (deltaX * deltaX + deltaY * deltaY > chordalDeviationSquared) { MakeLinesFromArc( x0, y0, x01, y01, midPointX, midPointY, vertexCountIndex, chordalDeviationSquared); MakeLinesFromArc( midPointX, midPointY, x12, y12, x2, y2, vertexCountIndex, chordalDeviationSquared); } else { /* * The "pen" is already at (x0, y0), so we don't need to * add that point to the LineBuf. */ if (!AppendToLineBuf(x1) || !AppendToLineBuf(y1) || !AppendToLineBuf(x2) || !AppendToLineBuf(y2)) return 0; LineBuf[vertexCountIndex] += 2.0F; } return 1; } /***************************************************************************** * InitLineBuf * * Initializes the global LineBuf and its associated size and current-element * counters. *****************************************************************************/ static int InitLineBuf(void) { if (!(LineBuf = (FLOAT*) __wglMalloc((LineBufSize = LINE_BUF_QUANT) * sizeof(FLOAT)))) return 0; LineBufIndex = 0; return 1; } /***************************************************************************** * InitVertBuf * * Initializes the global VertBuf and its associated size and current-element * counters. *****************************************************************************/ static int InitVertBuf(void) { if (!(VertBuf = (FLOAT*) __wglMalloc((VertBufSize = VERT_BUF_QUANT) * sizeof(FLOAT)))) return 0; VertBufIndex = 0; return 1; } /***************************************************************************** * AppendToLineBuf * * Appends one floating-point value to the global LineBuf array. Return value * is non-zero for success, zero for failure. *****************************************************************************/ static int AppendToLineBuf(FLOAT value) { if (LineBufIndex >= LineBufSize) { FLOAT* f; f = (FLOAT*) __wglRealloc(LineBuf, (LineBufSize += LINE_BUF_QUANT) * sizeof(FLOAT)); if (!f) return 0; LineBuf = f; } LineBuf[LineBufIndex++] = value; return 1; } /***************************************************************************** * AppendToVertBuf * * Appends one floating-point value to the global VertBuf array. Return value * is non-zero for success, zero for failure. * * Note that we can't realloc this one, because the tessellator is using * pointers into it. *****************************************************************************/ static int AppendToVertBuf(FLOAT value) { if (VertBufIndex >= VertBufSize) return 0; VertBuf[VertBufIndex++] = value; return 1; } /***************************************************************************** * FreeLineBuf * * Cleans up vertex buffer structure. *****************************************************************************/ static void FreeLineBuf(void) { if (LineBuf) { __wglFree(LineBuf); LineBuf = NULL; } } /***************************************************************************** * FreeVertBuf * * Cleans up vertex buffer structure. *****************************************************************************/ static void FreeVertBuf(void) { if (VertBuf) { __wglFree(VertBuf); VertBuf = NULL; } } /***************************************************************************** * GetWord * * Fetch the next 16-bit word from a little-endian byte stream, and increment * the stream pointer to the next unscanned byte. *****************************************************************************/ static long GetWord(UCHAR** p) { long value; value = ((*p)[1] << 8) + (*p)[0]; *p += 2; return value; } /***************************************************************************** * GetDWord * * Fetch the next 32-bit word from a little-endian byte stream, and increment * the stream pointer to the next unscanned byte. *****************************************************************************/ static long GetDWord(UCHAR** p) { long value; value = ((*p)[3] << 24) + ((*p)[2] << 16) + ((*p)[1] << 8) + (*p)[0]; *p += 4; return value; } /***************************************************************************** * GetFixed * * Fetch the next 32-bit fixed-point value from a little-endian byte stream, * convert it to floating-point, and increment the stream pointer to the next * unscanned byte. *****************************************************************************/ static double GetFixed( UCHAR** p) { long hiBits, loBits; double value; loBits = GetWord(p); hiBits = GetWord(p); value = (double) ((hiBits << 16) | loBits) / 65536.0; return value * ScaleFactor; } // ***********************************************************************